Compare commits

...

No commits in common. "v2.0.1" and "master" have entirely different histories.

284 changed files with 13603 additions and 18153 deletions

4
.gitattributes vendored
View file

@ -1,4 +0,0 @@
conf/* crlf=input
src/* crlf=input
hooks/common/* crlf=input
hooks/gitolite-admin/* crlf=input

5
.gitignore vendored
View file

@ -1,5 +0,0 @@
*.tar
*.tgz
*.tar.gz
*.tar.bz2
conf/VERSION

86
CHANGELOG Normal file
View file

@ -0,0 +1,86 @@
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.

View file

@ -276,64 +276,3 @@ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View file

@ -1,21 +0,0 @@
# this is a simple wrapper around "git archive" using make
# "make [refname].tar" produces a tar of refname, then adds a file containing
# the "git describe" output for that refname to the tar. This lets you say
# "cat .GITOLITE-VERSION" to find out which ref produced this tar
# Note: I'm not sure if that "-r" is a GNU tar extension...
branch := $(shell git rev-parse --abbrev-ref HEAD)
$(branch): $(branch).tar
.GITOLITE-VERSION:
@touch conf/VERSION
%.tar: .GITOLITE-VERSION
git describe --tags --long $* > conf/VERSION
git archive $* > $@
tar -r -f $@ conf/VERSION
rm conf/VERSION
cp -v $@ /tmp

359
README.md Normal file
View file

@ -0,0 +1,359 @@
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/

View file

@ -1,203 +0,0 @@
# main README file for gitolite -- start here!
<a name="start"></a>
***IMPORTANT NOTE: v2.0rc1 is out; all development will be on that now.
Please see new doc/developer-notes.mkd***
----
Gitolite is an access control layer on top of git, which allows access control
down to the branch level, including specifying who can and cannot *rewind* a
given branch.
Gitolite comes with a **huge** amount of documentation. If you're absolutely
new, the suggested reading order is this:
* the README (this document) for a quick intro
* the [INSTALL][install] document
* the [ADMIN][admin] document
If you run into trouble start [here](#support). If you're migrating from
gitosis, read [this][migr].
And [here][who]'s some information on some of the projects and people using
gitolite (and who, in turn, have helped shape its features).
Once you've installed it and started using it, you'll want to explore some of
the more powerful features. **The documentation is best read [online
here][docs] or you can read it in your clone's `doc/` directory**. All the
longer documents have tables of contents, so you can quickly get a feel for
what is covered right at the top.
----
In this document:
* <a href="#_what">what</a>
* <a href="#_why">why</a>
* <a href="#_main_features">main features</a>
* <a href="#_support">support</a>
* <a href="#_security">security</a>
* <a href="#_contact_and_license">contact and license</a>
----
<a name="_what"></a>
### what
Gitolite lets you use a single user on a server to host many git repositories
and provide access to many developers, without having to give them real
userids on or shell access to the server. The essential magic in doing this
is ssh's pubkey access and the `authorized_keys` file, and the inspiration was
an older program called gitosis.
Gitolite can restrict who can read from (clone/fetch) or write to (push) a
repository. It can also restrict who can push to what branch or tag, which is
very important in a corporate environment. Gitolite can be installed without
requiring root permissions, and with no additional software than git itself
and perl. It also has several other neat features described below and
elsewhere in the [doc/][docs] directory.
<a name="_why"></a>
### why
Gitolite is separate from git, and needs to be installed and configured. So...
why do we bother?
Gitolite is useful in any server that is going to host multiple git
repositories, each with many developers, where some sort of access control is
required.
In theory, this can be done with plain old Unix permissions: each user is a
member of one or more groups, each group "owns" one or more repositories, and
using unix permissions (especially the setgid bit -- `chmod g+s`) you can
allow/disallow users access to repos.
But there are several disadvantages here:
* every user needs a userid and password on the server. This is usually a
killer, especially in tightly controlled environments
* adding/removing access rights involves complex `usermod -G ...` mumblings
which most admins would rather not deal with
* *viewing* (aka auditing) 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 pretty much
impossible without extraneous tools
* errors or omissions in setting the permissions exactly can cause problems
of either kind: false accepts or false rejects
* without going into ACLs it is not possible to give someone read-only
access to a repo; they either get read-write access or no access
* it is absolutely impossible to restrict pushing by branch name or tag
name.
Gitolite does away with all this:
* it uses ssh magic to remove the need to give actual unix userids to
developers
* it uses a simple but powerful config file format to specify access rights
* access control changes are affected by modifying this file, adding or
removing user's public keys, and "compiling" the configuration
* this also makes auditing trivial -- all the data is in one place, and
changes to the configuration are also logged, so you can audit them.
* finally, the config file allows distinguishing between read-only and
read-write access, not only at the repository level, but at the branch
level within repositories.
<a name="_main_features"></a>
### main features
The most important feature I needed was **per-branch permissions**. This is
pretty much mandatory in a corporate environment, and is almost the single
reason I started *thinking* about writing gitolite.
It's not just "read-only" versus "read-write". Rewinding a branch (aka "non
fast forward push") is potentially dangerous, but sometimes needed. So is
deleting a branch (which is really just an extreme form of rewind). I needed
something in between allowing anyone to do it (the default) and disabling it
completely (`receive.denyNonFastForwards` or `receive.denyDeletes`).
Here're **some more features**. All of them, and more, are documented in
detail somewhere in gitolite's [doc/][docs] subdirectory.
* simple, yet powerful, config file syntax, including specifying
gitweb/daemon access. You'll need this power if you manage lots of
users+repos+combinations of access
* apart from branch-name based restrictions, you can also restrict by
file/dir name changed (i.e., output of `git diff --name-only`)
* if your requirements are still too complex, you can split up the config
file and delegate authority over parts of it
* easy to specify gitweb owner, description and gitweb/daemon access
* easy to sync gitweb (http) authorisation with gitolite's access config
* comprehensive logging [aka: management does not think "blame" is just a
synonym for "annotate" :-)]
* "personal namespace" prefix for each dev
* migration guide and simple converter for gitosis conf file
* "exclude" (or "deny") rights at the branch/tag level
* specify repos using patterns (patterns may include creator's name)
* define powerful operations on the server side, even github-like forking
<a name="support"></a>
<a name="_support"></a>
### support
Most installation problems are caused by not knowing ssh. Take a look at this
[transcript][] to see how simple it actually is, if your server's ssh daemon
is behaving itself. Someone also wrote a tutorial, see [here][tut].
If I suspect your problem is an ssh issue, I will probably ignore it. Please
learn how [gitolite uses ssh][doc9gas] and then methodically go through the
[ssh trouble shooting][doc6sts] document. These two documents contain
everything I could possibly tell you. I have nothing to add.
Even for other topics, please look through at least the table of contents of
at least the numbered documents to see if your question is already answered,
before asking.
<a name="_security"></a>
### security
Due to the environment in which this was created and the need it fills, I
consider this a "security" program, albeit a very modest one.
For the first person to find a security hole in it, defined as allowing a
normal user (not the gitolite admin) to read a repo, or write/rewind a ref,
that the config file says he shouldn't, and caused by a bug in *code* that is
in the "master" branch, (not in the other branches, or the configuration file
or in Unix, perl, shell, etc.)... well I can't afford 1000 USD rewards like
djb, so you'll have to settle for 5000 INR (Indian Rupees) as a "token" prize
:-)
However, there are a few optional features (which must be explicitly enabled
in the RC file) where I just haven't had the time to reason about security
thoroughly enough. Please read the comments in `conf/example.gitolite.rc` for
details, looking for the word "security".
----
<a name="_contact_and_license"></a>
### contact and license
Gitolite is released under GPL v2. See COPYING for details.
* author: sitaramc@gmail.com, sitaram@atc.tcs.com
* mailing list: gitolite@googlegroups.com
* list subscribe address : gitolite+subscribe@googlegroups.com
[transcript]: http://sitaramc.github.com/gitolite/doc/install-transcript.html
[install]: http://sitaramc.github.com/gitolite/doc/1-INSTALL.html
[admin]: http://sitaramc.github.com/gitolite/doc/2-admin.html
[migr]: http://sitaramc.github.com/gitolite/doc/migrate.html
[docs]: http://sitaramc.github.com/gitolite
[doc9gas]: http://sitaramc.github.com/gitolite/doc/gitolite-and-ssh.html
[doc6sts]: http://sitaramc.github.com/gitolite/doc/ssh-troubleshooting.html
[who]: http://sitaramc.github.com/gitolite/doc/who-uses-it.html
[tut]: http://sites.google.com/site/senawario/home/gitolite-tutorial

99
check-g2-compat Executable file
View file

@ -0,0 +1,99 @@
#!/usr/bin/perl
use Cwd;
my $h = $ENV{HOME};
my $rc = "$h/.gitolite.rc";
my %count;
intro();
msg( FATAL => "no rc file found; do you even *have* g2 running?" ) if not -f $rc;
do $rc;
unless ( $return = do $rc ) {
msg( FATAL => "couldn't parse $rc: $@" ) if $@;
msg( FATAL => "couldn't do $rc: $!" ) unless defined $return;
msg( WARNING => "couldn't run $rc" ) unless $return;
}
print "checking rc file...\n";
rc_basic();
rest_of_rc();
print "\n";
print "checking conf file(s)...\n";
conf();
print "\n";
print "checking repos...\n";
repo();
print "\n";
print "...all done...\n";
# ----------------------------------------------------------------------
sub intro {
msg( INFO => "This program only checks for uses that make the new g3 completely unusable" );
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( '', '' );
}
sub rc_basic {
msg( FATAL => "GL_ADMINDIR in the wrong place -- aborting; see docs" ) if $GL_ADMINDIR ne "$h/.gitolite";
msg( NOTE => "GL_ADMINDIR is in the right place; assuming you did not mess with" );
msg( '', "GL_CONF, GL_LOGT, GL_KEYDIR, and GL_CONF_COMPILED" );
msg( FATAL => "REPO_BASE in the wrong place -- aborting; see docs" ) if $REPO_BASE ne "$h/repositories" and $REPO_BASE ne "repositories";
# ( abs or rel both ok)
}
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_NO_CREATE_REPOS not yet implemented" ) if $GL_NO_CREATE_REPOS;
msg( SEVERE => "rsync not yet implemented" ) if $RSYNC_BASE;
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 => "non-default GL_WILDREPOS_PERM_CATS found" ) if $GL_WILDREPOS_PERM_CATS ne 'READERS WRITERS';
}
sub conf {
chdir($h);
chdir($GL_ADMINDIR);
my $conf = `find . -name "*.conf" | xargs cat`;
msg( "SEVERE", "NAME rules; see docs" ) if $conf =~ m(NAME/);
msg( "SEVERE", "subconf command in admin repo; see docs" ) if $conf =~ m(NAME/conf/fragments);
msg( "SEVERE", "mirroring used; see docs" ) if $conf =~ m(config +gitolite\.mirror\.);
}
sub repo {
chdir($h);
chdir($REPO_BASE);
my @creater = `find . -name gl-creater`;
if (@creater) {
msg( WARNING => "found " . scalar(@creater) . " gl-creater files; see docs" );
}
my @perms = `find . -name gl-perms | xargs egrep -l -w R\\|RW`;
if (@perms) {
msg( WARNING => "found " . scalar(@perms) . " gl-perms files with R or RW; see docs" );
}
}
sub msg {
my ( $type, $text ) = @_;
print "$type" if $type;
print "\t$text\n";
exit 1 if $type eq 'FATAL';
$count{$type}++ if $type;
}

View file

@ -1,72 +0,0 @@
# see doc/gitolite.conf.mkd for help on the syntax and semantics of this file
# online at http://sitaramc.github.com/gitolite/doc/gitolite.conf.html
# this file now has docs for just 2 features
# (1) NAME/ restrctions: documentation for this feature currently does not fit
# anywhere else, so it's still here
# FILE/DIR NAME BASED RESTRICTIONS
# --------------------------------
# Here's a hopefully self-explanatory example. Assume the project has the
# following contents at the top level: a README, a "doc/" directory, and an
# "src/" directory.
repo foo
RW+ = lead_dev # rule 1
RW = dev1 dev2 dev3 dev4 # rule 2
RW NAME/ = lead_dev # rule 3
RW NAME/doc/ = dev1 dev2 # rule 4
RW NAME/src/ = dev1 dev2 dev3 dev4 # rule 5
# Notes
# - the "NAME/" is part of the syntax; think of it as a keyword if you like.
# The rest of it is treated as a refex to match against each file being
# touched (see "SPECIFYING AND USING A REFEX" above for details)
# - file/dir NAME-based restrictions are *in addition* to normal (branch-name
# based) restrictions; they are not a *replacement* for them. This is why
# rule #2 (or something like it, maybe with a more specific branch-name) is
# needed; without it, dev1/2/3/4 cannot push any branches.
# - if a repo has *any* NAME/ rules, then NAME-based restrictions are checked
# for *all* users. This is why rule 3 is needed, even though we don't
# actually have any NAME-based restrictions on lead_dev. Notice the pattern
# on rule 3.
# - *each* file touched by the commits being pushed is checked against those
# rules. So, lead_dev can push changes to any files, dev1/2 can push
# changes to files in "doc/" and "src/" (but not the top level README), and
# dev3/4 can only push changes to files in "src/".
# (2) rsync helper: this is an oddball feature that only the person who asked
# me for is apparently using, created long ago, before I learned the value of
# saying "no" :-) These items just don't seem to fit in any of the existing
# documents.
# EXTERNAL COMMAND HELPERS -- RSYNC
# ---------------------------------
# If $RSYNC_BASE is non-empty, the following config entries come into play
# (otherwise they are ignored):
# a "fake" git repository to collect rsync rules. Gitolite does not
# auto-create any repo whose name starts with EXTCMD/
repo EXTCMD/rsync
# grant permissions to files/dirs within the $RSYNC_BASE tree. A leading
# NAME/ is required as a prefix; the actual path starts after that. Matching
# follows the same rules as given in "FILE/DIR NAME BASED RESTRICTIONS" above
RW NAME/ = sitaram
RW NAME/foo/ = user1
R NAME/bar/ = user2
# just to remind you that these are perl regexes, not shell globs
RW NAME/baz/.*/*.c = user3

View file

@ -1,93 +0,0 @@
# configuration variables for gitolite
# PLEASE READ THE DOCUMENTATION BEFORE EDITING OR ASKING QUESTIONS
# ( http://github.com/sitaramc/gitolite/blob/pu/doc/gitolite.rc.mkd )
# ( or http://sitaramc.github.com/gitolite/doc/gitolite.rc.html )
# this file is in perl syntax. However, you do NOT need to know perl to edit
# it; it should be fairly self-explanatory and easy to maintain
# ------------------------------------------------------------------------------
# DO NOT TOUCH THIS SECTION!
# ------------------------------------------------------------------------------
$GL_ADMINDIR=$ENV{HOME} . "/.gitolite";
$GL_CONF="$GL_ADMINDIR/conf/gitolite.conf";
$GL_KEYDIR="$GL_ADMINDIR/keydir";
$GL_CONF_COMPILED="$GL_ADMINDIR/conf/gitolite.conf-compiled.pm";
# DO NOT CHANGE THE NEXT FOUR LINES UNLESS YOU REALLY KNOW WHAT YOU'RE DOING.
# These variables are set automatically by the install method you choose.
# (PACKAGE MAINTAINERS: PLEASE READ doc/packaging.mkd)
# $GL_PACKAGE_CONF = "";
# $GL_PACKAGE_HOOKS = "";
# ------------------------------------------------------------------------------
# most often used/changed variables
# ------------------------------------------------------------------------------
$GL_WILDREPOS = 0;
$PROJECTS_LIST = $ENV{HOME} . "/projects.list";
$REPO_UMASK = 0077;
# ------------------------------------------------------------------------------
# variables with an efficiency/performance impact
# ------------------------------------------------------------------------------
$GL_BIG_CONFIG = 0;
$GL_NO_DAEMON_NO_GITWEB = 0;
# $GL_NICE_VALUE = 0;
# $BIG_INFO_CAP = 20;
# ------------------------------------------------------------------------------
# VARIABLES WITH A SECURITY IMPACT. READ DOCS BEFORE CHANGING THESE!
# http://github.com/sitaramc/gitolite/blob/pu/doc/gitolite.rc.mkd#_variables_with_a_security_impact
# (or http://sitaramc.github.com/gitolite/doc/gitolite.rc.html#_variables_with_a_security_impact)
# ------------------------------------------------------------------------------
# $GL_ALL_READ_ALL = 0;
$GIT_PATH="";
$GL_GITCONFIG_KEYS = "";
# $GL_GITCONFIG_WILD = 0;
$GL_NO_CREATE_REPOS = 0;
$GL_NO_SETUP_AUTHKEYS = 0;
# $GL_WILDREPOS_DEFPERMS = 'R @all';
$HTPASSWD_FILE = "";
$RSYNC_BASE = "";
$SVNSERVE = "";
# $UPDATE_CHAINS_TO = "hooks/update.secondary";
# $ADMIN_POST_UPDATE_CHAINS_TO = "hooks/post-update.secondary";
# $GL_ADC_PATH = "";
# $GL_GET_MEMBERSHIPS_PGM = "/usr/local/bin/expand-ldap-user-to-groups"
# $GL_HTTP_ANON_USER = "mob";
# ------------------------------------------------------------------------------
# less used/changed variables
# ------------------------------------------------------------------------------
# $GL_ALL_INCLUDES_SPECIAL = 0;
# $GL_SLAVE_MODE = 0;
# $ENV{GL_SLAVES} = 'gitolite@server2 gitolite@server3';
# PLEASE USE SINGLE QUOTES ABOVE, NOT DOUBLE QUOTES
$GL_WILDREPOS_PERM_CATS = "READERS WRITERS";
# $GL_SITE_INFO = "XYZ.COM DEVELOPERS: PLEASE SEE http://xyz.com/gitolite/help first";
# ------------------------------------------------------------------------------
# rarely changed variables
# ------------------------------------------------------------------------------
$GL_LOGT="$GL_ADMINDIR/logs/gitolite-%y-%m.log";
# $GL_PERFLOGT="$GL_ADMINDIR/logs/perf-gitolite-%y-%m.log";
# ------------------------------------------------------------------------------
# variables that should NOT be changed after the install step completes
# ------------------------------------------------------------------------------
$REPO_BASE="repositories";
# ------------------------------------------------------------------------------
# DO NOT TOUCH ANY THING AFTER THIS LINE
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# per perl rules, this should be the last line in such a file:
1;
# Local variables:
# mode: perl
# End:
# vim: set syn=perl:

View file

@ -1,57 +0,0 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
get_rights_and_owner gitolite-admin
[ -z "$perm_write" ] && die "just *what* are you trying to pull, young man?"
op=$1
shift
locs=
while [ -n "$1" ]
do
case $1 in
'@all' )
locs="$locs $HOME"
;;
* )
loc="$GL_REPO_BASE_ABS/$1.git"
[ -d $loc ] && locs="$locs $GL_REPO_BASE_ABS/$1.git"
[ -d $loc ] || echo "ignoring $1..."
;;
esac
shift
done
[ -z "$locs" ] && die "give me '@all' or some reponame"
case $op in
en|enable )
for l in $locs
do
rm -fv $l/.gitolite.down
done
;;
dis|disable )
TEMPDIR=$(mktemp -d -t tmp.XXXXXXXXXX)
export TEMPDIR
trap "/bin/rm -rf $TEMPDIR" 0
echo 'type the message to be shown to users when they try to push; end with Ctrl-D:'
echo > $TEMPDIR/msg
cat >> $TEMPDIR/msg
echo disabling following locations with message:
cat $TEMPDIR/msg
echo
for l in $locs
do
cat $TEMPDIR/msg > $l/.gitolite.down
echo $l
done
;;
* )
die "argument 1 must be 'en' or 'dis'"
;;
esac

View file

@ -1,56 +0,0 @@
#!/bin/sh
# please make sure this file is NOT chmod +x
# ------------------------------------------------------------------------------
# settings for various ADCs, collected in one place for ease of keeping local
# settings intact during upgrades (you only have to worry about this file
# now). Documentation for the variables, however, is in the respective ADC
# settings for 'rm' ADC
ARE_YOU_SURE=1
USE_LOCK_UNLOCK=1
# settings for 'trash' ADC
TRASH_CAN=$GL_REPO_BASE_ABS/deleted
TRASH_SUFFIX=`date +%Y-%m-%d_%H:%M:%S`
# settings for 'hub' ADC
BASE_FETCH_URL="git://gl.example.com"
GL_FORKED_FROM="gl-forked-from"
# KDE may set this to kde-cloned-from for historical reasons
# ------------------------------------------------------------------------------
# test an option value more concisely
opt() {
[ "$1" = "1" ] && return 0
return 1
}
valid_owned_repo() {
# check that an arg passed is a valid repo and the current user owns it
[ -z "$1" ] && die need a repo name
get_rights_and_owner $1
[ "$owner" = "$GL_USER" ] || die "$repo does not exist or is not yours!"
# and we sneak this in too, quietly :)
cd $GL_REPO_BASE_ABS
}
die() { echo "$@"; exit 1; }
# NOTE: this also sets $repo to the normalised (without .git suffix) reponame
get_rights_and_owner() {
local ans
repo=${1%.git}
ans=$(perl -I$GL_BINDIR -Mgitolite -e 'cli_repo_rights("'$repo'")')
# set shell variables as needed
owner=${ans#* }
rights=${ans% *}
echo $rights | grep C >/dev/null 2>&1 && perm_create=yes || perm_create=
echo $rights | grep R >/dev/null 2>&1 && perm_read=yes || perm_read=
echo $rights | grep W >/dev/null 2>&1 && perm_write=yes || perm_write=
}

View file

@ -1,37 +0,0 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
[ -z "$GL_RC" ] && die "ENV GL_RC not set"
[ -z "$2" ] && die "Usage: fork source_repo target_repo"
# get_rights_and_owner now also sets $repo; see comments in common functions
get_rights_and_owner $1; from=$repo
[ -z "$perm_read" ] && die "no read permissions on $from"
get_rights_and_owner $2; to=$repo
[ -z "$perm_create" ] && die "no create permissions on $to"
# clone $from to $to
git clone --bare -l $GL_REPO_BASE_ABS/$from.git $GL_REPO_BASE_ABS/$to.git
[ $? -ne 0 ] && exit 1
echo "$from forked to $to"
# fix up creator, gitweb owner, and hooks
cd $GL_REPO_BASE_ABS/$to.git
echo $GL_USER > gl-creater
git config gitweb.owner "$GL_USER"
( $GL_BINDIR/gl-query-rc GL_WILDREPOS_DEFPERMS ) |
SSH_ORIGINAL_COMMAND="setperms $to" $GL_BINDIR/gl-auth-command $GL_USER
cp -R $GL_REPO_BASE_ABS/$from.git/hooks/* $GL_REPO_BASE_ABS/$to.git/hooks
if [ -n "$GL_WILDREPOS_DEFPERMS" ]; then
echo "$GL_WILDREPOS_DEFPERMS" > gl-perms
fi
echo "$from" > gl-forked-from
# run gitolite's post-init hook if you can (hook code expects GL_REPO to be set)
export GL_REPO; GL_REPO="$to"
[ -x hooks/gl-post-init ] && hooks/gl-post-init

View file

@ -1,52 +0,0 @@
#!/usr/bin/perl
use strict;
use warnings;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
# get the repo name
my $repo = shift;
$repo =~ s/\.git$//;
# IMPORTANT NOTE: to do any of this inside a hook, you should just use
# $ENV{GL_REPO}, since it's guaranteed to be set to the right value
# to do a "level 1" check (repo level -- not branch level), do this:
my ($perm, $creator) = check_access($repo);
# you can pass in any repo name you wish instead of the active repo
# the first return value looks like one of these, so you can just check for
# the presence of "R" or "W" and be done:
# _____R___W_
# _____R_____
# ___________
# The second value is "<gitolite>" for a normal repo, an actual username for
# a wildrepo, or "<notfound>" for a non-existent repo.
# to do a "level 2" check (branches), do something like this
my $ret = check_access($repo, 'refs/heads/foo', 'W', 1);
# the 2nd argument must be a *full* refname (i.e., not "master", but
# "refs/heads/master"). The 3rd argument is one of W, +, C, or D. The 4th
# argument should be any non-false perl value, like 1.
# the return value may look like this:
# refs/.*
# or perhaps this, if you were denied
# DENIED by fallthru
# NOTE: do NOT pass "R" as the 3rd argument. It will seem to work because
# you're merely testing the permissions in this code, but an *actual* "git
# fetch" for even a DENIED ref will succeed if the user has read access to at
# least one branch. This is because the information on what ref is being read
# is not made available externally in any useful way (the way the "update"
# hook gets its arguments when a push happens).

View file

@ -1,123 +0,0 @@
#!/usr/bin/perl
# READ ALL INSTRUCTIONS **AND** SOURCE CODE BEFORE DEPLOYING.
# run arbitrary git commands on the server
# ----
# WARNING: HIGHLY INFLAMMABLE. FISSILE MATERIAL, RADIATION HAZARD. HANDLE
# WITH CARE. DO NOT REMOVE MANUFACTURER LABEL. NOT TO BE USED WHILE DRIVING
# OR UNDER THE INFLUENCE OF ALCOHOL. PATIENTS WITH HEART PROBLEMS MUST SEE
# THEIR CARDIOLOGIST BEFORE USING.
# ----
# ok, warnings done, here's the saner description.
#
# This ADC lets you run arbirtrary git commands on any repo on the server.
# The first argument will be the repo name, the second and subsequent
# arguments will be the rest of the git command. For example, to run `git
# describe --tags` on repo `foo`, you would run:
#
# ssh git@server git foo describe --tags
#
# If that looks weird to you, you can use
#
# ssh git@server git --repo=foo describe --tags
#
# (the position remains the same: between 'git' and '<command>')
# SECURITY AND SAFETY NOTES:
#
# - ADC arguments are checked (in `sub try_adc`) to fit `ADC_CMD_ARGS_PATT`
# and the only special characters allowed by that pattern are ".", "_", "@",
# "/", "+", ":", and "-". Thus, *this* adc does not check arguments
# anymore. ANY RISK IN THIS LAXITY IS YOURS, NOT MINE, although I believe
# it is safe enough.
#
# - Most commands don't make sense to allow, even among those that do not
# require a work-tree. Avoid commands that can be done using normal git
# remote access (ls-remote, clone, archive, push, etc). Also, avoid
# commands that *write* to the repo if possible, or at least think/test
# thoroughly before enabling them.
#
# - You have to deal with issues like stdin/out, output files created etc.,
# which is another reason to avoid most of the more complex commands.
#
# - Do not enable prune, gc, etc., if your repos are on NFS/CIFS/etc. See
# http://permalink.gmane.org/gmane.comp.version-control.git/122670 for why.
#
# - The list of commands allowed to be executed, and the permissions required
# to do so, are defined here. Feel free to uncomment any of this to make
# things more relaxed. If you add new ones, note that the permissions can
# only be 'R', 'W', or 'A'. The meanings of R and W are obvious; "A" means
# the user must have write access to the *gitolite-admin* repo to run this
# command -- yeah that's a nice twist innit? ;-)
my %GIT_COMMANDS = (
# annotate => 'R',
# blame => 'R',
'count-objects' => 'R',
describe => 'R',
# diff => 'R',
# 'fast-export' => 'R',
# grep => 'R',
# log => 'R',
# shortlog => 'R',
# 'show-branch' => 'R',
# show => 'R',
# whatchanged => 'R',
# config => 'A', # I strongly discourage un-commenting this
# fsck => 'W', # write access required
# gc => 'W', # write access required
# prune => 'A', # admin access required
# repack => 'A', # admin access required
);
# preliminary stuff; indented just to visually get it out of the way
use strict;
use warnings;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
my $no_help = "this command is too dangerous to just show a help message; we don't want anyone\nrunning it without reading the source and understanding the implications!\n";
# get the repo name
my $repo = shift or die $no_help;
$repo =~ s/^--repo=//;
$repo =~ s/\.git$//;
# get the command
my $cmd = shift or die $no_help;
# is it a valid command at all?
exists $GIT_COMMANDS{$cmd} or die "invalid git command\n";
# check access
my $aa = $GIT_COMMANDS{$cmd}; # aa == attempted access
if ($aa eq 'A') {
my ($perm, $creator) = check_access('gitolite-admin');
$perm =~ /W/ or die "no admin access\n";
} else {
my ($perm, $creator) = check_access($repo);
$perm =~ /$aa/ or die "no $aa access to $repo\n";
}
# cd to the repo dir
chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git") or die "chdir failed: $!\n";
# remove or comment the below line to signify you have read and understood all this
die $no_help;
# now run the git command... fingers crossed
unshift @ARGV, "git", $cmd;
print STDERR "+ ", join(" ", @ARGV), "\n";
exec @ARGV;

View file

@ -1,101 +0,0 @@
#!/usr/bin/perl -w
use strict;
use warnings;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
# - show fake "reflog" from gitolite server
# - recover deleted branches
# - recover from bad force pushes
# --------------------
# WARNING
# - heavily dependent on the gitolite log file format (duh!)
# - cannot recover if some other commits were made after the force push
sub usage {
print STDERR <<'EOF';
USAGE
ssh git@server gl-reflog show r1 refs/heads/b1
# shows last 10 updates to branch b1 in repo r1
ssh git@server gl-reflog show r1 refs/heads/b1 20
# shows last 20 entries...
ssh git@server gl-reflog recover r1 refs/heads/b1
# recovers the last update to b1 in r1 if it was a "+"
EOF
exit 1;
}
usage unless (@ARGV >= 3);
# NOTES
# - the verb "recover" is used because this is expected to be used most often
# to recover deleted branches. Plus there's enough confusion in git land
# caused by "reset" and "revert" I thought I should add my bit to it ;-)
# - git's internal reflog is NOT recovered, even if you recover the branch.
# I'm good but not *that* good ;-)
# - since this program produces a log entry that satisfies it's own criteria,
# it acts as a "toggle" for its own action for rewinds (but not for deletes)
my($cmd, $repo, $ref, $limit) = @ARGV;
$limit ||= 10;
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
my ($perm, $creator, $wild) = repo_rights($repo);
die "you don't have read access to $repo\n" unless $perm =~ /R/;
my @logfiles = sort glob("$ENV{GL_ADMINDIR}/logs/*");
# TODO figure out how to avoid reading *all* the log files when you really
# only need the last few
our @loglines;
{
my @f;
local(@ARGV) = @logfiles;
while (<>) {
chomp;
@f = split /\t/;
# field 2 is the userid, 5 is W or +, 6/7 are old/new SHAs
# 8 is reponame, 9 is refname (but all those are 1-based)
next unless $f[3] =~ /^(git-receive-pack|gl-reflog recover) /;
next unless $f[8];
next unless $f[7] eq $repo;
next unless $f[8] eq $ref;
push @loglines, $_;
}
}
if ( $cmd eq 'show' ) {
my $start = @loglines - $limit;
$start = 0 if $start < 0;
map { print "$loglines[$_]\n" } $start .. $#loglines;
exit 0;
}
if ( $cmd eq 'recover' ) {
my @f = split /\t/, $loglines[$#loglines];
die "the last push was not yours\n" unless $f[1] eq $ENV{GL_USER};
die "the last push was not a rewind or delete\n" unless $f[4] eq '+';
my($oldsha, $newsha) = @f[5,6];
if ($newsha =~ /^0+$/) {
print "recovering $repo $ref at $oldsha (was deleted)\n";
} else {
print "recovering $repo $ref at $oldsha (was forced to $newsha)\n";
}
chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git");
my $newsha2 = $newsha;
$newsha2 = '' if $newsha =~ /^0+$/;
system("git", "update-ref", $ref, $oldsha, $newsha2) and
die "repo $repo, update-ref $ref $oldsha $newsha failed...\n";
log_it("", "+\t$newsha\t$oldsha\t$repo\t$ref");
}

View file

@ -1,44 +0,0 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
# the local site can have a file called gl-adc-help.txt, which will be used as
# the *entire* help text for this site...
[ -f $HOME/gl-adc-help.txt ] && {
cat $HOME/gl-adc-help.txt
exit 0
}
# or the local site will use the default help text in this file, with an
# optional pre- and post- text that is site local (like maybe the admin's
# contact details)
# pre
[ -f $HOME/gl-adc-pre-help.txt ] && cat $HOME/gl-adc-pre-help.txt
# default help text
cat <<EOF
The following adc's (admin-defined commands) are available at this site.
creating a "fork" of a repo:
the 'fork' adc forks a repo that you have read access to, to a repo that
you have create rights to
deleting/trashing repos:
You can permanently remove a repo using 'rm'. By default, repos are
protected ('lock'ed) from being 'rm'-ed. You have to first 'unlock' a
repo before you can 'rm' it.
A different scheme of handling this is to use 'trash' to move the repo to
a "trashcan" area. You can then 'list-trash' to see what you have, and
you can then 'restore' whichever repo you need to bring back.
More details can be found at:
http://github.com/sitaramc/gitolite/blob/pu/contrib/adc/repo-deletion.README
EOF
# post
[ -f $HOME/gl-adc-post-help.txt ] && cat $HOME/gl-adc-post-help.txt

View file

@ -1,470 +0,0 @@
#!/usr/bin/perl -w
# SECURITY: look for the word SECURITY below and decide...
# handle pull-requests and related stuff
# developer notes:
# - 'requestor' is too long, so I use "bob"; if you see the documentation
# you'll realise this isn't as crazy as you think :-)
use strict;
use warnings;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
sub usage {
print STDERR <<'EOF';
GENERAL USAGE: ssh git@server hub <hub-command> <args>
See docs for concepts; this usage message is only a refresher!
Requestor's commands (repo child):
request-pull child b1 [parent]
request-status child [parent] [request-number]
Parent repo owner's commands (repo parent):
list-requests parent
view-request parent request-number
view-log parent request-number <git log options>
view-diff parent request-number <git diff options>
reject parent request-number
fetch parent request-number
accept parent request-number
EOF
exit 1;
}
our $tempdir;
END {
wrap_chdir($ENV{GL_REPO_BASE_ABS});
system("rm", "-rf", "$tempdir.git") if $tempdir and $tempdir =~ /gl-internal-temp-repo/;
}
my %dispatch = (
rp => \&rp,
'request-pull' => \&rp,
rs => \&rs,
'request-status' => \&rs,
lr => \&lr,
'list-requests' => \&lr,
vr => \&vr,
'view-request' => \&vr,
vl => \&vl,
'view-log' => \&vl,
vd => \&vd,
'view-diff' => \&vd,
reject => \&reject,
fetch => \&fetch,
accept => \&accept,
);
my $cmd = shift || '';
usage() unless ($dispatch{$cmd});
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
# find what is effectively GL_ADC_PATH, then get the config var we're interested in
use FindBin;
my $BASE_FETCH_URL = `. $FindBin::Bin/adc.common-functions; echo \$BASE_FETCH_URL`;
chomp($BASE_FETCH_URL);
my $GL_FORKED_FROM = `. $FindBin::Bin/adc.common-functions; echo \$GL_FORKED_FROM`;
chomp($GL_FORKED_FROM);
my @args = @ARGV; @ARGV = ();
$dispatch{$cmd}->(@args);
# -------------------- bob's commands
sub rp {
# request-pull child b1 [parent]
usage() unless @_ == 2 or @_ == 3;
# implicitly gives owner-parent read access to part of child, so requestor
# should already have read access to child (to prevent someone gaining
# access to child by faking a pull request against it!)
# XXX would it be better to ensure it is writable by Bob, because how/why
# would he make a pull request if he didn't just write to it?
my ($repo, $creator) = readable_repo(shift);
my $ref = valid_ref($repo, shift);
# the parent is either explicitly given, or the name of the parent
# recorded by the 'fork' ADC is used
my $repo_to = shift || parent_repo($repo);
# requestor need not have any access to parent; it is quite possible he
# gets this via git-daemon or something, so we just need to make sure it's
# a valid repo
$repo_to = valid_repo($repo_to);
# the 'cover letter' message comes from STDIN
my $cover = join("", <>);
# now create/update the pull request file
cd2repo($repo_to);
my %hub = get_hub();
$hub{$repo}{$ref}{BOB} = $ENV{GL_USER};
$hub{$repo}{$ref}{COVER} = $cover;
$hub{$repo}{$ref}{TIME} = time();
$hub{$repo}{$ref}{STATUS} = 'pending';
dump_hub(%hub);
}
sub rs {
# request-status child [parent] [request-number]
usage() unless @_ > 0 and @_ < 4; # 1 or 2 or 3
# same checks as in 'rp' above
my ($repo_from, $creator) = readable_repo(shift);
my $repo;
if ($_[0] and $_[0] !~ /^\d+$/) {
# next arg is not a number, so it should be 'parent'
$repo = shift;
} else {
$repo = parent_repo($repo_from);
}
$repo = valid_repo($repo);
my $rqno = 0;
$rqno = shift if ($_[0] and $_[0] =~ /^\d+$/);
# there should not be any arguments left over
usage() if @_;
unless ($rqno) {
cd2repo($repo);
my %hub_full = get_hub();
return unless $hub_full{$repo_from};
my %hub; $hub{$repo_from} = $hub_full{$repo_from};
list_hub('', %hub);
return;
}
my ($child, $ref, %hub) = get_request_N($repo, $rqno);
# this also does a chdir to $repo, by the way
my %hub1; $hub1{$child}{$ref} = $hub{$child}{$ref};
list_hub('', %hub1);
print "\nMessage:\n$hub1{$child}{$ref}{COVER}\n";
}
# -------------------- alice's commands
sub lr {
# list-requests parent [optional search strings]
usage() unless @_ >= 1;
# Alice must have write access to parent, otherwise she can't really
# accept a pull request eventually right?
my ($repo, $creator) = writable_repo(shift);
cd2repo($repo);
my %hub = get_hub();
return unless %hub;
# create the search pattern. ADC arg checking is very strict; it doesn't
# allow &, | etc., so we just generate an OR condition out of the pieces
my $patt = join("|", @_);
list_hub($patt, %hub);
}
sub vr {
# view-request parent request-number
usage() unless @_ == 2;
my ($repo, $n) = @_;
my ($child, $ref, %hub) = get_request_N($repo, $n);
# this also does a chdir to $repo, by the way
my %hub1; $hub1{$child}{$ref} = $hub{$child}{$ref};
list_hub('', %hub1);
print "\nMessage:\n$hub1{$child}{$ref}{COVER}\n";
}
sub vl {
# view-log parent request-number <git log options>
usage() unless @_ >= 2;
my ($repo, $n) = (shift, shift);
my ($child, $ref, %hub) = get_request_N($repo, $n);
# so now we can find the set of SHAs that we already have
# XXX should we include tags also?
my @known_shas = grep { chomp; } `git for-each-ref refs/heads --format='%(objectname)'`;
# make a copy of the child repo (Bob's repo) containing only the ref being
# offered for fetch, then cd to it. This is easier to do than to sanitise
# all possible git-log arguments. We're doing this to prevent Alice from
# seeing anything more than the ref offered.
temp_clone($child, $ref);
# verify the list of "known_shas" because what's known in Alice's repo may
# not be known here. While you're about it, negate them. (We don't want
# to use "--not" because we're not sure what arguments the user will want
# to add and we don't want to negate some of them by mistake
@known_shas = grep { $_ = `git rev-parse --verify -q $_`; chomp && s/^/^/ } @known_shas;
# run the log command
# XXX SECURITY XXX do we need to check these arguments? Don't forget they
# are restricted by $ADC_CMD_ARGS_PATT (defined in gitolite_rc.pm), which
# is pretty tight to start with, so we know this cannot be used to run
# external programs. The question is, are any of git-log's arguments
# dangerous in their own right?
my @args = ('git', 'log', $ref);
push @args, @known_shas if @known_shas;
check_SHAs($ref, @_);
# each SHA in @_ must be a parent of $ref. Non-shas are not allowed
# since all refs other than $ref have been deleted in the temp clone
push @args, @_ if @_;
system @args;
}
sub vd {
# view-diff parent request-number <git diff options>
usage() unless @_ >= 4;
# we just check for 4 arguments; I guess later on we could also check
# to make sure at least 2 of them are SHAs or something but unless
# there's a security risk it's not needed
my ($repo, $n) = (shift, shift);
my ($child, $ref, %hub) = get_request_N($repo, $n);
# this also does a chdir to $repo, by the way
# now go to the child repo (Bob's repo)
temp_clone($child, $ref);
# run the diff command
# XXX SECURITY XXX do we need to check these arguments? Don't forget they
# are restricted by $ADC_CMD_ARGS_PATT (defined in gitolite_rc.pm), which
# is pretty tight to start with, so we know this cannot be used to run
# external programs. The question is, are any of git-diff's arguments
# dangerous in their own right?
my @args = ('git', 'diff');
check_SHAs($ref, @_);
push @args, @_ if @_;
system @args;
}
sub reject {
# reject parent request-number
usage() unless @_ == 2;
my ($repo, $n) = @_;
writable_repo($repo); # yeah we're throwing away the return values
my ($child, $ref, %hub) = get_request_N($repo, $n);
map { die "request status is already '$_'\n" if $_ ne 'pending' } $hub{$child}{$ref}{STATUS};
# the 'cover letter' message comes from STDIN
my $cover = join("", <>);
$hub{$child}{$ref}{STATUS} = "rejected by $ENV{GL_USER}";
$hub{$child}{$ref}{COVER} .= "\n\nRejected. Message to requestor:\n$cover";
dump_hub(%hub);
}
sub fetch {
# fetch parent request-number
usage() unless @_ == 2;
my ($repo, $n) = @_;
writable_repo($repo); # yeah we're throwing away the return values
my ($child, $ref, %hub) = get_request_N($repo, $n);
map { die "request status is already '$_'\n" if $_ ne 'pending' } $hub{$child}{$ref}{STATUS};
print "user $hub{$child}{$ref}{BOB} asked you to\n\tgit fetch $BASE_FETCH_URL/$child $ref\n";
print "hit enter to accept the fetch request or Ctrl-C to cancel...";
<>;
my $fetched_ref = "refs/heads/requests/child/$ref";
# you're already chdir'd to parent, by get_request_N
system("git", "update-ref", "-d", "refs/heads/$fetched_ref");
system("git", "fetch", "$ENV{GL_REPO_BASE_ABS}/$child.git", "$ref:$fetched_ref");
$hub{$child}{$ref}{STATUS} = "fetched by $ENV{GL_USER}";
dump_hub(%hub);
}
sub accept {
# accept parent request-number
usage() unless @_ == 2;
my ($repo, $n) = @_;
writable_repo($repo); # yeah we're throwing away the return values
my ($child, $ref, %hub) = get_request_N($repo, $n);
map { die "request status is '$_'; must be 'fetched'\n" if $_ !~ /^fetched by / } $hub{$child}{$ref}{STATUS};
# the 'cover letter' message comes from STDIN
my $cover = join("", <>);
$hub{$child}{$ref}{STATUS} = "accepted by $ENV{GL_USER}";
$hub{$child}{$ref}{COVER} .= "\n\nAccepted. Message to requestor:\n$cover";
dump_hub(%hub);
}
# -------------------- service subs
sub assert {
my ($expr, $message) = @_;
eval $expr or die ($message ? "$message\n" : "assert '$expr' failed\n");
}
sub cd2repo {
my $repo = shift;
wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git");
}
sub dump_hub {
# pwd assumed to git repo.git; dump a file called "gl-adc-hub-requests"
use Data::Dumper;
$Data::Dumper::Indent = 1;
$Data::Dumper::Sortkeys = 1;
my %hub = @_;
my $fh = wrap_open(">", "gl-adc-hub-requests");
print $fh Data::Dumper->Dump([\%hub], [qw(*hub)]);
close $fh;
}
sub get_hub {
# pwd assumed to git repo.git; "do" a file called "gl-adc-hub-requests"
return () unless -w "gl-adc-hub-requests";
our %hub = ();
do "gl-adc-hub-requests" or die "error parsing gl-adc-hub-requests\n";
return %hub;
}
sub get_request_N {
# given a repo and an N, return "child", "ref", and %hub (or die trying!)
# you can't look at pull requests for repos you don't have at least read access to
my ($repo, $creator) = readable_repo(shift);
cd2repo($repo);
my %hub = get_hub();
die "you have no pending requests\n" unless %hub;
my $n = shift || '';
usage() unless ($n =~ /^\d+$/);
my @hub = hub_sort(%hub);
die "you have only " . scalar(@hub) . " requests\n" if @hub < $n;
$n--; # make it 0-relative
return ($hub[$n]->{REPO}, $hub[$n]->{REF}, %hub);
}
sub hub_sort {
my %hub = @_;
my %sorted_hub = ();
for my $child (sort keys %hub) {
for my $ref (sort keys %{ $hub{$child} }) {
my $key = $hub{$child}{$ref}{TIME} . "-$child-$ref";
$sorted_hub{$key} = { REPO=>$child, REF=>$ref };
}
}
my @hub = ();
for my $key (sort keys %sorted_hub) {
push @hub, $sorted_hub{$key};
}
return @hub;
}
sub list_hub {
my ($status, %hub) = @_;
my $header = "#\tchild-repository-name\t(requestor)\tbranch-or-tag-to-pull\tstatus\n----\n";
my @hub = hub_sort(%hub);
my $sn = 0;
for my $pr (@hub) {
$sn++;
my $child = $pr->{REPO};
my $ref = $pr->{REF};
my $pr_status = $hub{$child}{$ref}{STATUS};
next if $status and $pr_status !~ /$status/;
print $header if $header; $header = '';
print "$sn\t$child\t($hub{$child}{$ref}{BOB})\t$ref\t$pr_status\n";
}
}
sub parent_repo {
my ($repo) = shift;
cd2repo($repo);
die "parent repo was not recorded, sorry!\n" unless -f $GL_FORKED_FROM;
my $gff = `cat $GL_FORKED_FROM`;
chomp($gff);
return $gff;
}
sub readable_repo {
my $repo = valid_repo(shift);
my ($perm, $creator) = check_access($repo);
die "$repo does not exist or you have no read access\n" unless $perm =~ /R/;
return ($repo, $creator);
}
sub valid_log_options {
}
sub valid_ref {
my ($repo, $ref) = @_;
cd2repo($repo);
die "invalid ref $ref\n" unless `git cat-file -t $ref` =~ /^commit$/;
die "invalid ref $ref\n" unless `git rev-parse $ref` =~ /^[0-9a-f]{40}$/;
return $ref;
}
sub valid_repo {
my $repo = shift;
$repo =~ s/\.git$//;
die "$repo does not exist or you have no read access\n" unless -d "$ENV{GL_REPO_BASE_ABS}/$repo.git";
return $repo;
}
sub writable_repo {
my $repo = valid_repo(shift);
my ($perm, $creator) = check_access($repo);
die "$repo does not exist or you have no write access\n" unless $perm =~ /W/;
return ($repo, $creator);
}
sub temp_clone {
my ($repo, $ref) = @_;
die "internal error; temp_clone called twice?\n" if $tempdir;
# some of this code is also in "rrr" branch
# first make a temp directory within $REPO_BASE
$ENV{TMPDIR} = $ENV{GL_REPO_BASE_ABS};
$tempdir = `mktemp -d -t gl-internal-temp-repo.XXXXXXXXXX`;
chomp($tempdir);
rename $tempdir, "$tempdir.git";
# make the clone
wrap_chdir("$ENV{GL_REPO_BASE_ABS}");
system("git clone --mirror -l $repo.git $tempdir.git >/dev/null 2>&1");
# go to the clone and delete refs he's not allowed to read
wrap_chdir("$ENV{GL_REPO_BASE_ABS}");
wrap_chdir("$tempdir.git");
# for each available ref
for my $ar (`git for-each-ref refs '--format=%(refname)'`) {
chomp($ar);
system('git', 'update-ref', '-d', $ar) unless $ar eq "refs/heads/$ref";
}
# you've already cd-d to the temp repo, just set the name up properly
$tempdir =~ s/^\Q$ENV{GL_REPO_BASE_ABS}\///;
}
sub check_SHAs {
my $ref = shift;
for (@_) {
next unless /^[0-9a-f]+$/i;
my $fullsha = `git rev-parse $_`;
chomp($fullsha);
die "invalid SHA: $_\n" unless $fullsha =~ /^[0-9a-f]{40}$/;
my $mergebase = `git merge-base $fullsha $ref`;
chomp($mergebase);
die "invalid SHA: $_\n" unless $mergebase eq $fullsha;
}
}

View file

@ -1,208 +0,0 @@
# the 'hub' ADC
In this document:
* <a href="#_a_home_grown_hub_for_git_repos">a home grown 'hub' for git repos</a>
* <a href="#_general_syntax">general syntax</a>
* <a href="#_Bob_s_commands">Bob's commands</a>
* <a href="#_Alice_s_just_looking_commands">Alice's "just looking" commands</a>
* <a href="#_Alice_s_action_commands">Alice's "action" commands</a>
* <a href="#_what_next_">what next?</a>
* <a href="#_note_to_the_admin_configuration_variables">note to the admin: configuration variables</a>
<a name="_a_home_grown_hub_for_git_repos"></a>
### a home grown 'hub' for git repos
This ADC (admin-defined command) helps collaboration among repos. The name is
in honor of github, which is the primary host for gitolite itself.
[Note that github is a web-based service, and does a lot more, like comments,
code reviews, etc., none of which are possible here. We're only talking about
some basic stuff to make the most common operations easier. In particular,
this system is not a replacement for normal project communications like
email!]
**Conventions used**: all through the following description, we will assume
that **Alice** has a repo **parent**, **Bob** has a fork of parent called
**child**, and he is asking Alice to pull a branch he made called **b1** from
child to parent.
In plain git (without using github or similar), the pull request process
starts with an email from Bob to Alice, followed by Alice running a `git fetch
<Bob's URL> b1` (optionally preceded by a `git remote add` for convenience of
long term use). Until this happens she can't see much detail about the
commits to be pulled.
**What this ADC does** is (a) collect all the pull requests she needs to look
at in one place, and (b) allow her to examine the changes with any combination
of `git log` and `git diff` options *without having to fetch the content
first*, and (c) act as a trusted intermediary to allow Alice to fetch *just
one branch* from Bob even if she does not have any access to Bob's repository!
In a situation where there may be lots of requests, being able to take a quick
look at them (and possibly reject some), without having to pull down anything
at all, could be very useful.
Once past that level, she could get the changes down to her workstation using
`git fetch` as normal. With this ADC, however, she has another alternative:
get them over to `parent` (her repo) on the same gitolite server, then later
do a normal `git fetch [origin]` to get it to her workstation. This has the
added advantage that other people, who may be watching her repo but not Bob's,
now get to see what Bob sent her and send comments etc.
<a name="_general_syntax"></a>
### general syntax
The general syntax is
ssh git@server hub <hub-command> <args>
<a name="_Bob_s_commands"></a>
#### Bob's commands
The following commands do not cause a fetch, and should be quite fast:
* Bob sends a pull request for branch b1 to repo parent. Notice he does not
mention Alice by name. This command expects a message to be piped/typed
in via STDIN [this message is meant to be transient and is not stored long
term; use email for more "permanent" communications].
echo "hi Alice, please pull" | ssh git@server hub request-pull child b1 [parent]
If `child` was created by a recent version of the 'fork' ADC (or the KDE
'clone' ADC), which records the name of the parent repo on a fork, and it
is *that* repo to which Bob wishes to send the pull request, the third
argument is optional.
* Bob lists the status (fetched/rejected/pending) of pull requests he has
made from his repo child to repo parent. (Note we don't say "accepted" but
"fetched"; see later for why):
ssh git@server hub request-status child [parent]
The second argument is optional the same way as the 3rd argument in the
previous command.
Requests that have been accepted or rejected will usually have some
additional text, supplied by the user who did the reject/accept. Bob can
ask for those details by request number:
ssh git@server hub request-status child [parent] request-number
<a name="_Alice_s_just_looking_commands"></a>
#### Alice's "just looking" commands
* Alice lists requests waiting for her to check and possibly pull into
parent. For each waiting pull request, she will see a serial number, the
originating repo name (child, in our example), the requestor (Bob, here),
and the branch/tag-name (b1) being pulled:
ssh git@server hub list-requests parent
This command also takes an optional list of search strings that are OR-d
together and matched against the 'status' field. So saying
ssh git@server hub list-requests parent fetched pending
would list only items that were 'fetched' or 'pending' (meaning 'accepted'
and 'rejected' would not show up).
* Alice views request # 1 waiting to be pulled into parent. Shows the same
details as above for that request, followed by the message that Bob typed
in when he ran `request-pull`:
ssh git@server hub view-request parent 1
* Alice views the log of the branch she is being asked to pull. Note that
this does NOT involve a fetch, so it will be pretty fast. The log starts
from b1, and stops at a SHA that represents any of the branches in parent.
Alice can use any git-log options she wants to; for instance `--graph`,
`--decorate`, `--boundary`, etc., could be quite useful. However, she
can't use any GUI; it has to be 'git log':
ssh git@server hub view-log parent 1 <git log options>
Notice that the repo name Alice supplies is still her own, although the
log comes from the child repo that Bob wants Alice to pull from. It's
best if you think of this as "view the commits from pull request #1 in
'parent'".
* Alice views the diff between arbitrary commits on child:
ssh git@server hub view-diff parent 1 <git diff options>
Again, she mentions *her* reponame but the diff's come from `child`. Also
note that, due to restrictions on what characters are allowed in arguments
to ADCs, you probably can't do things like `pu^` or `master~3`, and have
to use SHAs instead.
<a name="_Alice_s_action_commands"></a>
#### Alice's "action" commands
* Alice doesn't like what she sees and decides to reject it. This command
expects some text on STDIN as the rejection message:
echo "hi Bob, your patch needs work; see email" | ssh git@server hub reject parent 1
* Alice likes what she sees so far and wants to fetch the branch Bob is
asking her to pull. Note that we are intentionally not using the word
"accept", because this command does not (and cannot, since it is running
on a bare repo on the server) do a pull. What it does is it fetches into
a branch whose name will be `requests/child/b1`.
Note that when multiple requests from the same repo (child) for the same
branch (b1) happen, each "fetch" overwrites the branch. This allows Bob
to continually refine the branch he is requesting for a pull based on
(presumably emailed) comments from Alice. In a way, this is a "remote
tracking branch", just like `refs/remotes/origin/b1`.
ssh git@server hub fetch parent 1
This command will actually fetch from child into parent, and may take time
when the changes are large. However all this is on the server so it does
not involve network traffic:
* Alice has fetched the stuff she wants, looked at it/tested it, and decides
to merge it into `parent`. Once that is done, she runs:
echo "thanks for the frobnitz patch Bob" | ssh git@server hub accept parent 1
to let Bob know next time he checks 'request-status'. Like the `reject`
sub-command, this is also just a status update; no actual 'git' changes
happen.
Notice the sequence of Alice's action commands: it's either 'reject', or a
'fetch' then 'accept'.
<a name="_what_next_"></a>
### what next?
At this point, you're done with the `hub` ADC. However, all this is on the
bare `parent.git` on the server, and nothing has hit Alice's workstation yet!
Alice will still have to run a fetch or a pull on her workstation if she wants
to check the code in detail, use a tool like gitk, and especially to
compile/test it. *Then* she decides whether to accept or reject the request,
which will have to be communicated via email, of course; see the second para
of this document ;-)
Finally, note that Alice does not actually need to use the `fetch` subcommand.
She can do the traditional thing and fetch Bob's repo/branch directly to her
*workstation*.
<a name="_note_to_the_admin_configuration_variables"></a>
### note to the admin: configuration variables
There are 2 configuration variables. `BASE_FETCH_URL` should be set to a
simple "read" URL (so it doesn't even have to be ssh) that almost anyone using
this server can use. It's only used in producing the `git fetch` command
mentioned just above.
`GL_FORKED_FROM` is set to `gl-forked-from` by default, but if your initials
are `JM` you can set it to `kde-cloned-from` to save time and trouble ;-)

View file

@ -1,13 +0,0 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
# this is a helper ADC for "trash"; see that one for option settings etc
cd $TRASH_CAN 2>/dev/null || exit 0
find . -name gl-creater | sort | while read t
do
owner=
owner=`cat "$t"`
[ "$owner" = "$GL_USER" ] && dirname $t
done | cut -c3-

View file

@ -1,12 +0,0 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
# this is a helper ADC for "rm"; see that one for documentation
# cd to repo base and make sure arg1 is a valid repo (also sets $repo)
valid_owned_repo $1
rm -f $repo.git/gl-rm-ok
echo "$repo has been locked. Please run the 'help' adc for more info."

View file

@ -1,115 +0,0 @@
#!/usr/bin/env python
#
# Original author: Richard Bateman <taxilian@gmail.com>
#
# Any questions or concerns about how this works should be addressed to
# me, not to sitaram. Please note that neither I nor sitaram make any
# guarantees about the security or usefulness of this script. It may
# be used without warantee or any guarantee of any kind.
#
# That said, it works fine for me.
#
# This script is licensed under the New BSD license
# Copyright 2011 Richard Bateman
#
import sys, os
from pygitolite import *
def list(gl, user, repo, filter_var = ""):
perms = gl.get_perms(repo, user)
for var, ppl in perms.iteritems():
if filter_var == "" or filter_var == var:
print "%s:" % var
for item in ppl:
print " %s" % item
def clear(gl, user, repo, filter_var = ""):
try:
os.system(r"echo Are you sure? Type YES \(all caps\) to continue: ")
bval = raw_input()
if bval != "YES":
print "Canceling..."
if filter_var == "":
gl.set_perms(repo, user, {})
else:
perms = gl.get_perms(repo, user)
if filter_var in perms:
del perms[filter_var]
gl.set_perms(repo, user, perms)
print "Perms after clear:"
list(gl, user, repo)
except:
print "An error occured"
def add(gl, user, repo, var, *users):
perms = gl.get_perms(repo, user)
if var not in perms:
perms[var] = []
if len(users) == 0:
print "Usage: perms add %s %s <username>" % (repo, var)
return
for cur in users:
if cur not in perms[var]:
perms[var].append(cur)
gl.set_perms(repo, user, perms)
list(gl, user, repo, var)
def set(gl, user, repo, var, *users):
perms = gl.get_perms(repo, user)
perms[var] = []
if len(users) == 0:
print "Usage: perms set %s %s <username>" % (repo, var)
return
for cur in users:
if cur not in perms[var]:
perms[var].append(cur)
gl.set_perms(repo, user, perms)
list(gl, user, repo, var)
def remove(gl, user, repo, var, *users):
perms = gl.get_perms(repo, user)
if var not in perms:
print "%s isn't a valid type" % var
return
if len(users) == 0:
print "No users specified to remove; perhaps you want clear?"
return
for cur in users:
if cur in perms[var]:
perms[var].remove(cur)
gl.set_perms(repo, user, perms)
list(gl, user, repo, var)
commands = {
"list": list,
"clear": clear,
"add": add,
"set": set,
"remove": remove,
}
if __name__ == "__main__":
if "GL_USER" not in os.environ:
raise "No user!"
user = os.environ["GL_USER"]
command = sys.argv[1] if len(sys.argv) > 2 else ""
if len(sys.argv) < 3 or command not in commands:
print "Usage: perms <command> <repository> <args>"
print " list <repository> [TYPE]"
print " clear <repository>"
print " add <repository> <TYPE> [user and group list]"
print " set <repository> <TYPE> [user and group list]"
print " remove <repository> <TYPE> [user and group list]"
sys.exit(1)
repo = sys.argv[2]
gl = gitolite()
rights, owner = gl.get_rights_and_owner(repo, user)
if owner != user:
print "Either %s does not exist or you are not the owner." % repo
sys.exit(1)
commands[command](gl, user, repo, *sys.argv[3:])

View file

@ -1,77 +0,0 @@
#!/usr/bin/env python
#
# Original author: Richard Bateman <taxilian@gmail.com>
#
# Any questions or concerns about how this works should be addressed to
# me, not to sitaram. Please note that neither I nor sitaram make any
# guarantees about the security or usefulness of this script. It may
# be used without warantee or any guarantee of any kind.
#
# This script is licensed under the New BSD license
# Copyright 2011 Richard Bateman
#
import sys, os, subprocess
class gitolite(object):
def __init__(self, **kvargs):
self.GL_BINDIR = kvargs["GL_BINDIR"] if "GL_BINDIR" in kvargs else os.environ["GL_BINDIR"]
self.user = kvargs["GL_USER"] if "GL_USER" in kvargs else os.environ["GL_USER"]
pass
def gitolite_execute(self, command, std_inputdata = None):
cmd = "perl -I%s -Mgitolite -e '%s'" % (self.GL_BINDIR,command)
p = subprocess.Popen(cmd, shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE, stdin = subprocess.PIPE)
stdout, stderr = p.communicate(std_inputdata)
if p.returncode is not 0:
raise Exception(stderr)
return stdout.strip()
def run_custom_command(self, repo, user, command, extra = None):
os.environ["SSH_ORIGINAL_COMMAND"] = "%s %s" % (command, repo)
return self.gitolite_execute('run_custom_command("%s")' % user, extra)
def get_perms(self, repo, user):
full = self.run_custom_command(repo, user, "getperms")
plist = full.split("\n")
perms = {}
for line in plist:
if line == "":
continue
var, strlist = line.split(" ", 1)
perms[var] = strlist.split(" ")
return perms
def set_perms(self, repo, user, perms):
permstr = ""
for var, curlist in perms.iteritems():
if len(curlist) == 0:
continue;
varstr = var
for cur in curlist:
varstr += " %s" % cur
permstr = permstr + "\n" + varstr
resp = self.run_custom_command(repo, user, "setperms", permstr.strip())
def valid_owned_repo(self, repo, user):
rights, user = self.get_rights_and_owner(repo, user)
return owner == user
def get_rights_and_owner(self, repo, user):
if not repo.endswith(".git"):
repo = "%s.git" % repo
ans = self.gitolite_execute('cli_repo_rights("%s")' % repo)
perms, owner = ans.split(" ")
rights = {"Read": "R" in perms, "Write": "W" in perms, "Create": "C" in perms}
return rights, owner
if __name__ == "__main__":
if "GL_USER" not in os.environ:
raise "No user!"
user = os.environ["GL_USER"]
repo = sys.argv[1]
gl = gitolite()
print gl.get_rights_and_owner(repo, user)
print gl.get_perms(repo, user)

View file

@ -1,48 +0,0 @@
# deleting repos safely
(see http://groups.google.com/group/gitolite/browse_thread/thread/fb9cf5a464b6dfee )
By default, the old 'rmrepo' ADC (admin-defined command) just went and deleted
the repo -- no questions asked! Sometimes, that could be a disaster -- you
lose the whole thing in one mad moment of typo-ing or frustration. Ouch.
This has been replaced by 2 families of ADCs. I say "families" because each
has one main command and 2 ancillary ones. Admins can choose to install
either, both, or neither family of commands.
Local settings for these ADCs can be found in the common settings file
"adc.common-functions".
1. 'rm' will remove the repo. If USE_LOCK_UNLOCK is set, rm will refuse to
remove a locked repo. All repos are locked by default, and you have to
explicitly 'unlock' a repo to remove it. You can also 'lock' it again
instead of removing it of course.
There's also ARE_YOU_SURE, for situations where a simple warning suffices.
You can also use both these flags if you wish.
2. 'trash' will move the repo to a safe location. There are settings for
where this location is and what suffix is added to the repo name. You can
'list-trash' to see what trash you have collected, and you can 'restore'
one of the listed repos.
It's easy to automatically clean out the trash occasionally. By default,
entries in the trash look like this:
foo/r1/2010-10-22_13:14:24
foo/r1/2010-10-22_13:14:50
This shows a repo foo/r1 that was created and trashed twice.
Since the date appears in the name, you can use it with a cutoff to clean
up old repos. Untested example:
cutoff=`date -I -d '28 days ago'`
find $TRASH_CAN -type d -name "20??-??-??_*" | while read r
do
d=`basename $r`
[[ $d < $cutoff ]] && rm -rf $r
done
Put this in cron to run once a day and that should be it.

View file

@ -1,16 +0,0 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
repo=$1
[ -z "$1" ] && die need a repo name
owner=
owner=`cat $TRASH_CAN/$repo/gl-creater 2>/dev/null`
[ "$owner" = "$GL_USER" ] || die "$repo is not yours!"
cd $TRASH_CAN
realrepo=`dirname $repo`
[ -d $GL_REPO_BASE_ABS/$realrepo.git ] && die $realrepo already exists
mv $repo $GL_REPO_BASE_ABS/$realrepo.git
echo $repo restored to $realrepo

View file

@ -1,11 +0,0 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
get_rights_and_owner gitolite-admin
[ -z "$perm_write" ] && die "just *what* are you trying to pull, young man?"
# and here you let them do the dangerous stuff
echo "+rm -rf $GL_REPO_BASE_ABS"
sleep 2
echo ...just kidding!

View file

@ -1,39 +0,0 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
[ -z "$GL_RC" ] && die "ENV GL_RC not set"
# options settable in adc.common-functions are
# ARE_YOU_SURE -- prompts "are you sure?"
# USE_LOCK_UNLOCK -- allows delete only if repo is "unlock"ed
# As shipped, both options are set. If you set both of them to "0", repos are
# just deleted blindly, with no confirmation
# helper ADCs: lock, unlock
# cd to repo base and make sure arg1 is a valid repo (also sets $repo)
valid_owned_repo $1
opt $USE_LOCK_UNLOCK && {
if [ -f $repo.git/gl-rm-ok ]
then
:
else
die "$repo is locked! Please run the 'help' adc for more info."
fi
}
opt $ARE_YOU_SURE && {
echo "Are you sure? (type 'yes' if you are)" >&2
read s
[ $s = "yes" ] || die aborting...
}
rm -rf $repo.git
echo "$repo is now GONE!"
cd $HOME
PROJECTS_LIST=$($GL_BINDIR/gl-query-rc PROJECTS_LIST)
export repo
perl -ni -e 'print unless /^\Q$ENV{repo}.git\E$/' $PROJECTS_LIST

View file

@ -1,4 +0,0 @@
#!/bin/bash
echo "this command no longer exists. Please run the 'help' adc for more info."
exit 1

View file

@ -1,150 +0,0 @@
#!/usr/bin/perl
# Copyright 2011, David Bremner <bremner@debian.org>
#
# This add-on to gitolite is licensed under the same terms as gitolite
# At the time of this writing this is GPL-2, but I also grant
# Sitaram Chamarty the right to re-license as he chooses.
=pod
S3BACKUP - Backup the whole gitolite home directory to amazon s3.
RUNNING
To run it (assuming you call this "s3backup")
As an ADC, only by a user with read access to gitolite-admin :
ssh git@server s3backup [-v error|warning|notice|info|debug] [subcommands]
You must pass the AWS_SECRET_ACCESS_KEY on stdin.
SUBCOMMANDS
You may optionally pass one of the following sub commands
=over
=item incr|full
Run incremental or full backup. By default, leave it to duplicity to
guess.
=item prune
remove all but $S3_KEEP_FULL (see Configuration below)
=back
OPTIONS
-v specifies a log level, passed straight to duplicity.
incr or full specify backup level
CONFIGURATION
Make a file ~git/.s3backup.rc that looks like
$S3_EUROPE=0; # 1 to store in europe
# (only matters at creation)
$S3_KEEP_FULL=3; # keep 3 full backups
$S3_BUCKET="my_bucket"; # s3 bucket name, will be created
# if it doesn't exist.
$S3_AWS_KEY_ID="ABADABA57DOO"; # this is the _non_ secret one
$S3_ENCRYPT_KEY="DEADBEEF AA232332"; # gpg keys, space delimited.
# note that duplicity will abort if
# these keys are not trusted
=cut
use strict;
use warnings;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
my ($perm, $creator) = check_access("gitolite-admin");
die "no read access to gitolite-admin\n" unless $perm =~ /R/;
our ($S3_EUROPE, $S3_BUCKET, $S3_AWS_KEY_ID, $S3_ENCRYPT_KEYS, $S3_KEEP_FULL);
chdir($ENV{HOME}) or die "chdir $ENV{HOME} : $!\n";
my $configfile = $ENV{HOME} . "/.s3backup.rc";
do $configfile or die "error parsing $configfile\n";
# ANCHOR ALL PATTERNS
die 'bad value for $S3_EUROPE'
unless (defined($S3_EUROPE) && $S3_EUROPE =~ m/^0|1$/);
die 'bad value for $S3_KEEP_FULL'
unless (defined($S3_KEEP_FULL) && $S3_KEEP_FULL =~ m/^[0-9]+$/);
die 'bad value for $S3_BACKUP'
unless (defined($S3_BUCKET) && $S3_BUCKET =~ m/^[a-z\-_0-9\.]+$/);
die 'bad value for $S3_AWS_KEY_ID'
unless (defined($S3_AWS_KEY_ID) && $S3_AWS_KEY_ID =~ m/^[A-Z0-9]+$/);
die 'bad value for $S3_ENCRYPT_KEYS'
unless (defined($S3_ENCRYPT_KEYS) &&
$S3_ENCRYPT_KEYS =~ m/^[a-fA-F0-9]+(\s+[a-fA-F0-9]+)*/);
my $verbosity='notice';
if (scalar(@ARGV)>0 and $ARGV[0] eq '-v'){
shift;
$verbosity = shift;
}
die "bad verbosity" unless ($verbosity =~ m/^(error|warning|notice|info|debug)$/);
my $subcommand=shift || 'default';
die "bad subcommand" if (defined($subcommand) &&
$subcommand !~ m/^(incr|full|prune|default)$/);
$ENV{AWS_ACCESS_KEY_ID}=$S3_AWS_KEY_ID;
chomp($ENV{AWS_SECRET_ACCESS_KEY}=<>) or
die "must pass SECRET_ACCESS_KEY on stdin";
my @args=();
if ($subcommand ne 'default' ){
if ($subcommand eq 'prune') {
push(@args, 'remove-all-but-n-full', $S3_KEEP_FULL, '--force');
} else {
push(@args, $subcommand);
}
}
push(@args, '--verb', $verbosity);
push(@args, '--s3-use-new-style');
foreach my $key (split(' ',$S3_ENCRYPT_KEYS)){
push(@args, '--encrypt-key', $key);
}
push(@args, '--s3-european-buckets') if ($S3_EUROPE);
push(@args, $ENV{HOME}) unless ($subcommand eq 'prune');
push(@args, 's3+http://'.$S3_BUCKET);
my $semaphore=$ENV{HOME}."/.gitolite.down";
die "$semaphore already exists" if (-f $semaphore);
eval {
open (SEMFD,'>',$semaphore) or die ("failed to open $semaphore");
my $now = gmtime();
print SEMFD "Repo unavailable due to $subcommand backup started at $now GMT\n";
close SEMFD;
system '/usr/bin/duplicity', @args;
};
unlink $semaphore;

View file

@ -1,17 +0,0 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
[ -z "$GL_RC" ] && die "ENV GL_RC not set"
[ -z "$2" ] && die "usage: set-head /path/to/repo.git refs/heads/branchname"
get_rights_and_owner $1; to=$repo
[ -z "$perm_write" ] && die "no write permissions on $to"
# change head
cd $GL_REPO_BASE_ABS/$to.git
git symbolic-ref HEAD $2
echo HEAD on $to is `git symbolic-ref HEAD`
cd - > /dev/null

View file

@ -1,56 +0,0 @@
#!/bin/sh
# adc for someone with admin privs to invoke "expand" on other users.
# This doc block as "WHY", "HOW", and "CAVEATS" sections; I mention that only
# for those people with very small xterms who may miss the CAVEATS section
# otherwise...!
# WHY
# ===
# ...because info has it, and expand doesn't :-)
# (Possible question: Why not add that capability to expand instead to be
# consistent? Probable answer: how about we go the other way and make
# "info" also work like this, and remove that code from core? I like
# having less less code in core!)
# HOW
# ===
# ssh gitolite su-expand . user1 user2 user3
# note that the first argument is still treated as a "pattern", so at
# least put in a "." for a catch-all pattern. Also see caveats for
# patterns below.
# This will output the same thing as "ssh git@server expand ." performed
# by the individual users
# CAVEATS
# =======
# (1) LIMITS TO THE PATTERN
# Due to this being an ADC, and ADC arguments being very, Very, VERY,
# stricly limited, you won't be able to use any regex meta characters
# expect ".". I'll worry about changing it if enough people complain.
# (2) NAME OF THIS SCRIPT
# please name this script something other than "expand", because we can't
# have name clashes between internal commands (info, expand,
# (get|set)(perms|desc), etc) and admin-defined (external) commands. I'm
# calling it su-expand; feel free to change it.
. $(dirname $0)/adc.common-functions
get_rights_and_owner gitolite-admin
[ -z "$perm_write" ] && die "just *what* are you trying to pull here, $GL_USER?"
pat="$1"; shift
for user
do
SSH_ORIGINAL_COMMAND="expand $pat" $GL_BINDIR/gl-auth-command $user
done

View file

@ -1,24 +0,0 @@
#!/bin/sh
# this command is pretty cool, even if I may say so myself :)
# for any ADC that a normal user can run, like
# ssh git@server adc arguments
# this adc lets a "super user" (defined as "have write access to the
# gitolite-admin repo"), do this
# ssh git@server sudo normal_user adc arguments
. $(dirname $0)/adc.common-functions
get_rights_and_owner gitolite-admin
[ -z "$perm_write" ] && die "just *what* are you trying to pull, young man?"
user="$1"; shift
cmd="$1"; shift
GL_USER=$user; export GL_USER
[ -x $(dirname $0)/$cmd ] || die "no adc called $cmd"
exec $(dirname $0)/$cmd "$@"

View file

@ -1,29 +0,0 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
# options settable in adc.common-functions are
# TRASH_CAN -- where the trashed repos are moved. Can be anywhere that the
# hosting user has write access to. Does not have to be (and ideally
# should NOT be) inside $GL_REPO_BASE_ABS (but see note below)
# TRASH_SUFFIX -- a timestamp, (ideally and by default), to be
# suffixed to the moved repo
# helper ADCs: list-trash, restore
# NOTE: although I would NOT advise it in the interests of keeping things
# simple, it *is* possible to have even deleted repos be *directly* accessible
# via normal gitolite mechanisms (clone, etc). Here's how:
# (1) make TRASH_CAN point somewhere *within* $REPO_BASE
# (2) change TRASH_SUFFIX to something that has a .git at the end, like:
# TRASH_SUFFIX=`date +%Y-%m-%d-%H-%M-%S`.git
# (3) set ACL rules in conf/gitolite.conf for repos named
# deleted/foo/sitaram/bar.*
# cd to repo base and make sure arg1 is a valid repo (also sets $repo)
valid_owned_repo $1
mkdir -p $TRASH_CAN/$repo 2>/dev/null || die "failed creating directory in trashcan"
[ -d $TRASH_CAN/$repo/$TRASH_SUFFIX ] && die try again in a few seconds
mv $repo.git $TRASH_CAN/$repo/$TRASH_SUFFIX
echo "$repo moved to trashcan. Please run the 'help' adc for more info."

View file

@ -1,12 +0,0 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
# this is a helper ADC for "rm"; see that one for documentation
# cd to repo base and make sure arg1 is a valid repo (also sets $repo)
valid_owned_repo $1
touch $repo.git/gl-rm-ok
echo "$repo has been unlocked. Please run the 'help' command for more info."

View file

@ -1,85 +0,0 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
# Copyright 2010 Massachusetts Institute of Technology
#
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
# This "watch" ADC can be used to maintain a list of watchers to a
# repository. You can then perform actions on the watchlist from a
# hook or ADC.
#
# An obvious function of this would be to maintain a list of email
# addresses that are sent commit notifications. There are however
# unlimited uses, such as:
#
# * Specifying Twitter accounts that should receive notifications
# of new clones created via the "fork" ADC
# * Specifying hostnames of machines that should be poked to update
# their repository web views
# * Etc...
#
# The parameters are the repository path, "add"/"remove", and the
# identifier. These are added along with your system identifier to a
# gl-watchers file. However, there is an optional argument that allows you
# specify what the identifier is used for. This allows you to perform
# a default action if there is no optional argument, or a specific action
# depending on what the optional argument. For instance, the gl-watchers
# file on the server might look like this (note the actions described would
# only occur if you actually code it in!):
#
# mitchell mitchell@kde.org [no optional argument; would default to email]
# mitchell mitchell@kde.org email [will send email on pushes]
# mitchell projects.kde.org httppost [will send commit details in an
# HTTP POST to proejcts.kde.org]
#
#
# Note that there is no authentication here; any user is allowed to
# set any identifier, although users can only modify their own
# identifiers. (This applies of course only to repositories a user can read
# in the first place!) This script can easily be modified to only allow only
# administrators (those with write access to the gitolite
# administration repository) to add or remove watchers. See the
# commented code below; uncomment it to enable this functionality.
# Adminstrators can use the "sudo" ADC to add entries for specific
# users.
# Uncomment this and comment the corresponding block below to
# restrict this functionality to administrators only
#get_rights_and_owner gitolite-admin
#[ -z "$perm_write" ] && die "just *what* are you trying to pull, young man?"
#get_rights_and_owner $1;
# Comment this block if uncommenting the above block
get_rights_and_owner $1;
[ -z "$perm_read" ] && die "no read permissions on $repo"
cmd=$2
identifier=$3
arg=$4
[ -z "$cmd" -o -z "$identifier" -o "$cmd" != "add" -a "$cmd" != "remove" ] && die "paramters must be <repopath> <add|remove> <identifier> <optional argument>"
if [ ! -z "$arg" ]
then
identarg="$identifier $arg"
else
identarg="$identifier"
fi
cd $GL_REPO_BASE_ABS/$repo.git
[ ! -e gl-watchers ] && { touch gl-watchers || die "cannot create blank watchers file"; }
[ ! -r gl-watchers ] && die "cannot read watchers file"
grep "^$GL_USER $identarg$" gl-watchers > /dev/null
found=$?
[ $found -eq 0 -a $cmd == "add" ] && die "There is already a watch \"$identarg\" for user $GL_USER"
[ $found -ne 0 -a $cmd == "remove" ] && die "No watch \"$identarg\" found for user $GL_USER"
[ $cmd == "add" ] && echo "$GL_USER $identarg" >> gl-watchers && { echo "Added a watch \"$identarg\" for user $GL_USER"; exit 0; }
[ $cmd == "remove" ] && sed -i -e "/^$GL_USER $identarg$/d" gl-watchers && { echo "Removed a watch \"$identarg\" for user $GL_USER"; exit 0; }
die "16 cores, 320GB of RAM, 4TB of disk, and you give me a command I am not programmed to do. Humans..."

View file

@ -1,33 +0,0 @@
#!/bin/bash
# find the last person to push the given commit
# XXX we assume the logfile names have been left as default, or at least, if
# changed, in such a way that when sorted by "ls", they come up oldest first
. $(dirname $0)/adc.common-functions
sha=$2
[ -n "$sha" ] || die Usage: ssh ... who-pushed reponame SHA \# at least first few hex digits
# get_rights_and_owner now also sets $repo; see comments in common functions
get_rights_and_owner $1
[ -z "$perm_read" ] && die "no read permissions on $repo"
cd $GL_REPO_BASE_ABS/$repo.git
logdir=$(dirname $GL_LOG) # uncodumented env var ;-)
ls $logdir | tac | while read lf
do
< $logdir/$lf perl -ne "print if /receive-pack.*\s$repo\s/" | cut -f1,2,3,5- | tac
done | while read ts who IP perm old new repo ref rule
do
save_old=$old
[ "$new" = "00000000000000" ] && continue
[ -z "$old" ] && continue
[ "$old" = "00000000000000" ] && old=
[ -n "$old" ] && old=$old..
git rev-list $old$new 2>/dev/null | grep ^$sha >/dev/null &&
printf "$ts $who $IP $perm $save_old $new $repo $ref $rule\n"
done

View file

@ -1,122 +0,0 @@
## comparing gerrit and gitolite
Gerrit and gitolite have too many high level differences. Size is most
visible of course: 56000 lines of Java versus 1300 lines of perl+shell,
according to David A. Wheeler's 'SLOCCount' tool. Gerrit needs a database (it
comes with a perfectly usable one, or I believe you can use any of the usual
suspects),
and even comes with its own ssh server and git server, and since the git
engine is internal it probably has to include a lot of things that normal git
already has; I wouldn't know for sure.
Gerrit allows a lot more de-centralisation in managing the system, and of
course excels at code review, and it seems geared to really large, open
source-ish projects with lots of contributors.
Gitolite works on a pure command-line install and a plain text file config,
and is designed to run unobtrusively and quite transparently to all developers
-- other than sending the admin their pubkey, nothing really changes for them
in their workflow, toolset, etc. The "lite" in the name still holds, despite
all the extra features being pumped in!
Gitolite was mainly written for a corporate environment, where we really,
really, need branch-level ACLs. While they would certainly love the code
review part, things like *voting* on a change, and so on seem a bit alien, and
it seems to me that code review itself is more likely to be a hierarchical
thing, not a peer-to-peer, "anyone can comment" thing. I could be wrong.
----
In short, gitolite doesn't do the main thing that gerrit does, and gerrit is
so much bigger than gitolite in so many ways, it seems really odd to compare
them at all.
However, it seems gerrit comes closest to gitolite in terms of flexibility of
access control, which is gitolite's main strength, so I thought it would be
useful to compare gitolite with just what is in the "access-control.html" in
the gerrit war file. Or see [this][gdac]. [...and stop sniggering at the
"svn" in the link dammit!]
Note that I don't necessarily list something that both tools have. (For
example, per-user branches denoted by `/USER/` in gitolite and `$(username)`
in gerrit, or being able to distinguish between creating a branch and pushing
to an already created one, etc).
[gdac]: http://gerrit.googlecode.com/svn/documentation/2.1.2/access-control.html
[jwzq]: http://regex.info/blog/2006-09-15/247
**Administrators**: anyone who has gitolite-admin push privs
**Anonymous Users**: gitolite doesn't do that, though the "ssh-plus" branch,
combined with git-daemon2 (Ilari) will allow that in future. When git-daemon2
becomes mainstream, the supporting code in this branch will also be merged
into "master".
**Registered Users**: @all
**Account Groups**: @groups in gitolite. We do allow them to be nested,
although the parsing is single-pass. We also don't have group `foo-admin`
managing membership to group `foo` though; all groups are managed by the eqvt
of "Administrators".
**Project ACLs**: first, let's remember (again) that we don't have any of the code
review stuff :)
* This is a subjective point, but gerrit doesn't give permissions to
individual users, only groups. People who have several small projects
(only one QA, one integrator, etc.), would have to create lots of 1-man
groups, which could be cumbersome.
* **Evaluation** order and priority of access control rules are different.
Gerrit goes by specificity, gitolite goes by sequence. It shouldn't
matter; they're probably equivalent except perhaps in some far-fetched
scenarios.
* One big difference is that gitolite does not process "deny" rules ("-1 no
Access" in gerrit terms) for *read* access -- we only support those for
write access. Gerrit uses this to "hide a handful of projects on an
otherwise public server"; in gitolite you'd better avoid giving `R = @all`
in the first place :)
* Update 2010-10-24: as per [this][gitlog1] Gerrit now has *read* access
control at the branch level -- they can afford to do that because they
have a full jgit stack to play with. Even then it was not easy -- they
had to implement a callback from jgit to gerrit for the fetch, *and* deal
with evil clients that might try to read an object by *pushing* a supposed
change on top of a SHA that they know but don't actually have. (You'll
have to think about this carefully; it may not be immediately obvious to
people who do not know the ref-exchange in the git protocol).
Gitolite is dependent on git itself to provide that -- it just cannot be
done without support from git core. I can see some corporates drooling at
this possibility (makes no sense for open source projects IMO) ;-)
My normal recommendation is to **use separate repos** if you really need
this while continuing to use gitolite. Much simpler and easier to audit
and to convince auditors that "those people can't see that code".
**Categories**:
* gitolite doesnt have an "owner" for each project in any administrative
sense. Perhaps you could consider whoever has `RW+` perms to be an owner
but it doesn't go beyond what that implies.
* gitolite doesnt do anything special to signed or annotated tags
* Force push is the same as delete by default. However, gitolite can now
allow these two separately if that's how you need it.
Of course, direct pushing clashes with code review, and gerrit recommends
that if you want code review you should not use this feature. [Normal
pushes in gerrit go through a temp branch that is moved to the correct one
after a review is done; direct pushes are all that gitolite has].
* author/committer identity: checking these fields in pushed commits is
likely to be important in some projects, but gitolite doesn't have any
notion of this. Hmm... I smell another feature in the future :)
The rest of it is in areas that the two tools have no overlap on (again, code
review being the main thing), or, as I said at the top, features that both
tools have.
[gitlog1]: http://colabti.org/irclogger/irclogger_log/git?date=2010-09-17#l2710

View file

@ -1,15 +0,0 @@
## gitolite-tools
gitolite-tools is a collection of external git commands to work with
gitolite server and repositories:
* git gl-info - Display gitolite server information
* git gl-ls - List accessible gitolite repositories
* git gl-desc - Display or edit description of gitolite wildcard repositories
* git gl-perms - Display or edit permissions of gitolite wildcard repositories
* git gl-htpasswd - Set password for gitweb/apache
## Homepage
The project in GitHub:
[http://github.com/tmatilai/gitolite-tools](http://github.com/tmatilai/gitolite-tools)

View file

@ -1,53 +0,0 @@
# --------------------------------------------
# Per-repo authorization based on gitolite ACL
# Include this in gitweb.conf
# See doc/3-faq-tips-etc.mkd for more info
# please note that the author does not have personal experience with gitweb
# and does not use it. Some testing may be required. Patches welcome but
# please make sure they are tested against a "github" version of gitolite
# and not an RPM or a DEB, for obvious reasons.
# HOME of the gitolite user
my $gl_home = $ENV{HOME} = "/home/git";
# the following variables are needed by gitolite; please edit before using
# this should normally not be anything else
$ENV{GL_RC} = "$gl_home/.gitolite.rc";
# this can have different values depending on how you installed.
# If you installed using the 'from-client' method it will be this:
$ENV{GL_BINDIR} = "$gl_home/.gitolite/src";
# if you used RPM/DEB or "root" methods it **might** be this:
$ENV{GL_BINDIR} = "/usr/local/bin";
# if you used the "non-root" method it **might** be this:
$ENV{GL_BINDIR} = "$gl_home/bin";
# If in doubt take a look at ~/.ssh/authorized_keys; at least one of the lines
# might contain something like:
# command="/home/git/.gitolite/src/gl-auth-command
# and you should use whatever directory the gl-auth-command is in (in this
# example /home/git/.gitolite.src)
# finally the user name
$ENV{GL_USER} = $cgi->remote_user || "gitweb";
# now get gitolite stuff in...
unshift @INC, $ENV{GL_BINDIR};
require gitolite_rc; gitolite_rc -> import;
require gitolite; gitolite -> import;
# set project root etc. absolute paths
$ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$gl_home/$REPO_BASE" );
$projects_list = $projectroot = $ENV{GL_REPO_BASE_ABS};
$export_auth_hook = sub {
my $repo = shift;
# gitweb passes us the full repo path; so we strip the beginning
# and the end, to get the repo name as it is specified in gitolite conf
return unless $repo =~ s/^\Q$projectroot\E\/?(.+)\.git$/$1/;
# check for (at least) "R" permission
my ($perm, $creator) = &repo_rights($repo);
return ($perm =~ /R/);
};

View file

@ -1,20 +0,0 @@
## ldap helper programs
These programs were contributed by the Nokia MeeGo folks.
The first 2 are perl and shell verisions of programs meant to be used as
`$GL_GET_MEMBERSHIPS_PGM` (see [this][ldap] for more).
* ldap-query-example.pl
* ldap-query-example.sh
The third program is meant to be installed as an adc (admin-defined command,
see [here][adc]), and helps users change their LDAP passwords.
* passwd
Enjoy!
[ldap]: http://sitaramc.github.com/gitolite/doc/big-config.html#_storing_usergroup_information_outside_gitolite_like_in_LDAP_
[adc]: http://sitaramc.github.com/gitolite/doc/admin-defined-commands.html

View file

@ -1,80 +0,0 @@
#!/usr/bin/perl
#
# Copyright (c) 2010 Nokia Corporation
#
# This code is licensed to you under MIT-style license. License text for that
# MIT-style license is as follows:
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ldap-query.pl <arg1>
#
# this script is used to perform ldap querys by giving one argument:
# - <arg1> the user UID for ldap search query
#
# NOTICE: This script requires libnet-ldap-perl package to be installed
# to the system.
#
use Net::LDAP;
# Script requires user UID as the only parameter
if ( $ARGV[0] eq '' || $ARGV[1] ne '' )
{
print "ldap-query.pl requires one argument, user's uid\n";
exit 1;
}
$user = $ARGV[0];
# Create communication structure for LDAP connection
$ldap = Net::LDAP->new(
'localhost',
port => 389,
debug => 0,
timeout => 120,
version => 3 ) or die "$@";
# Bind to LDAP with proper user
$ldapret = $ldap->bind( 'cn=administrator,o=company',
password => '5ecretpa55w0rd' );
die "$ldapret->code" if $ldapret->code;
# Create filter for LDAP query
my $filter = '(&'.
'(objectClass=groupAttributeObjectClassName)'.
"(uid=$user)".
')';
# Execute the actual LDAP search to get groups for the given UID
$ldapret = $ldap->search( base => 'ou=users,ou=department,o=company',
scope => 'subtree',
filter => $filter );
# Parse search result to get actual group names
my $default_group = '';
my $extra_groups = '';
foreach my $entry ( $ldapret->entries ) {
$default_group = $entry->get_value( 'defaultGroupAttributeName' ) . ' ' . "$default_group";
$extra_groups = $entry->get_value( 'extraGroupsAttributeName' ) . ' ' . "$extra_groups";
}
# Return group names for given user UID
print "$default_group" . "$extra_groups";

View file

@ -1,68 +0,0 @@
#!/bin/sh
#
# Copyright (c) 2010 Nokia Corporation
#
# This code is licensed to you under MIT-style license. License text for that
# MIT-style license is as follows:
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ldap-query.sh <arg1>
#
# this script is used to perform ldap querys by giving one argument:
# - <arg1> the user UID for ldap search query
#
# NOTICE: This script requires ldap-utils and sed to be installed to the system.
#
# Script requires user UID as the only parameter
#
if [ $# -ne 1 ]
then
echo "ldap-query.sh requires one argument, user's uid"
exit 1
fi
uid_param="${1}"
# Set needed LDAP search tool options for the query
ldap_host="localhost"
ldap_binddn="cn=administrator,o=company"
ldap_bindpw="5ecretpa55w0rd"
ldap_searchbase="ou=users,ou=department,o=company"
ldap_scope="subtree"
# Construct the command line base with needed options for the LDAP query
ldap_options="-h ${ldap_host} -x -D ${ldap_binddn} -w ${ldap_bindpw} -b ${ldap_searchbase} -s ${ldap_scope}"
# Construct the search filter for the LDAP query for the given UID
ldap_filter="(&(objectClass=groupAttributeObjectClassName)(uid=${uid_param}))"
# Construct return attribute list for LDAP query result
attr1="defaultGroupAttributeName"
attr2="extraGroupsAttributeName"
ldap_attr="${attr1} ${attr2}"
# Execute the actual LDAP search to get groups for the given UID
ldap_result=$(ldapsearch ${ldap_options} -LLL ${ldap_filter} ${ldap_attr})
# Edit search result to get space separated list of group names
ldap_result=$(echo ${ldap_result} | sed -e "s/.* ${attr1}://" -e "s/ ${attr2}://")
# Return group names for given user UID
echo ${ldap_result}

View file

@ -1,112 +0,0 @@
#!/usr/bin/perl
use Net::LDAP;
use Term::ReadPassword;
use Digest::SHA1;
use MIME::Base64;
use Data::UUID;
use Crypt::Cracklib;
my $PASSWD_MIN_LEN = 8;
my $password;
# parse RC file
# $ENV{GL_RC} = "/home/gitolite/.gitolite.rc";
die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC};
# These come from .gitolite.rc file
our ($GL_LDAP_HOST, $GL_LDAP_BIND_DN, $GL_LDAP_BIND_PASSWORD, $GL_LDAP_USER_DN);
$Term::ReadPassword::ALLOW_STDIN = 1;
# NOTICE: For some reason Perl fails to disable terminal echo
# so following warning about ECHO must be given to the user
# Warn about password echo because of bugs in Perl ReadPasword
print "\nNOTE THAT THE PASSWORD WILL BE ECHOED TO THE SCREEN!\n" .
"Please make sure no one is shoulder-surfing, and make sure\n" .
"you clear your screen and scrollback history after you are done\n" .
"(or close your terminal session).\n\n";
print "Please type in your new password at the prompt.\n\n" .
"Following special keys are available while typing:\n" .
" <BackSpace> key to remove the last character\n" .
" <Ctrl-U> to remove all characters\n" .
" <Ctrl-C> to terminate password change operation\n" .
" <Enter> to end password typing\n";
while ( 1 ) {
print "\n"; # Start reading with new line
$password = read_password("Enter new password: ", 0, 1);
# Check the validity of new password
if ( length( $password ) >= $PASSWD_MIN_LEN # require minimum length
&& $password =~ /([\x20-\x7E])/ # require printable characters
&& $password =~ /[a-z]/ # require lower case letter
&& $password =~ /[A-Z]/ # require upper case letter
&& $password =~ /[0-9]/ # require number
&& check( $password ) ) # require other than dictionary words
{
# Re-enter new password to check possible typos
if ( $password ne read_password("Enter password again: ") ) {
print "Passwords do not match!\n";
redo;
} else {
last; # Password is valid and there are no typos, so break out
}
} else { # Given password is not valid
print "Password must contain at least $PASSWD_MIN_LEN characters and numbers,\n" .
"must have both upper and lower case characters,\n" .
"can have special characters like !,",#,...\n" .
"but cannot be any valid dictionary word.\n";
redo;
}
}
# Create hash from the password to be stored to the LDAP
my $ctx = Digest::SHA1->new();
my $ug = new Data::UUID;
my $salt = $ug->create_b64();
$ctx->add( $password );
$ctx->add( $salt );
$password = '{SSHA}' . encode_base64( $ctx->digest . $salt, '' );
# Create communication structure for LDAP connection
my $ldap = Net::LDAP->new( $GL_LDAP_HOST ) or die "$@";
my $r = $ldap->start_tls( verify => 'none',
sslversion => 'tlsv1' );
if ( $r->code ) {
print "Password handling failed with $r->code return code!\n";
log_it( "Password change, LDAP connection failed for $ENV{GL_USER}" );
exit 1;
}
# Bind to LDAP with proper user
$r = $ldap->bind( $GL_LDAP_BIND_DN,
password => $GL_LDAP_BIND_PASSWORD );
if ( $r->code ) {
print "Password update failed with $r->code return code!\n";
log_it( "Password change, LDAP bind failed for $ENV{GL_USER}" );
exit 1;
}
# Update new password to the LDAP
$r = $ldap->modify( "uid=$ENV{GL_USER},
$GL_LDAP_USER_DN",
replace => { 'userPassword', $password } );
if ( $r->code ) {
print "Password change failed!\n" .
"Please contact administrator to change password.\n";
# log_it( "Password change, LDAP modify failed for $ENV{GL_USER}" );
} else {
print "Password changed succesfully.\n";
# log_it( "Password change, LDAP modify done for $ENV{GL_USER}" );
}
$r = $ldap->unbind();

View file

@ -1,248 +0,0 @@
## putty and msysgit
In this document:
* <a href="#_msysgit_setup">msysgit setup</a>
* <a href="#_Going_back_to_OpenSSH">Going back to OpenSSH</a>
* <a href="#_Putty_keys">Putty keys</a>
* <a href="#_Creating_a_new_key">Creating a new key</a>
* <a href="#_Importing_an_existing_key">Importing an existing key</a>
* <a href="#_Loading_an_existing_key">Loading an existing key</a>
* <a href="#_Public_key">Public key</a>
* <a href="#_Putty_ageant">Putty ageant</a>
* <a href="#_Sessionless_or_raw_hostname_usage">Sessionless or raw hostname usage</a>
* <a href="#_Putty_sessions">Putty sessions</a>
* <a href="#_Host_key_authentication">Host key authentication</a>
* <a href="#_Debugging_multiple_putty_ageant_keys">Debugging multiple putty ageant keys</a>
* <a href="#_Setperms_and_other_commands">Setperms and other commands</a>
* <a href="#_About_this_document">About this document</a>
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](http://sitaramc.github.com/gitolite/doc/ssh-troubleshooting.html).**
<a name="msysgit_setup"/>
<a name="_msysgit_setup"></a>
### 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"/>
<a name="_Going_back_to_OpenSSH"></a>
### 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"/>
<a name="_Putty_keys"></a>
### 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"/>
<a name="_Creating_a_new_key"></a>
#### 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"/>
<a name="_Importing_an_existing_key"></a>
#### 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"/>
<a name="_Loading_an_existing_key"></a>
#### 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"/>
<a name="_Public_key"></a>
#### 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"/>
<a name="_Putty_ageant"></a>
#### 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"/>
<a name="_Sessionless_or_raw_hostname_usage"></a>
### 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"/>
<a name="_Putty_sessions"></a>
### 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"/>
<a name="_Host_key_authentication"></a>
### 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"/>
<a name="_Debugging_multiple_putty_ageant_keys"></a>
### 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"/>
<a name="_Setperms_and_other_commands"></a>
### 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"/>
<a name="_About_this_document"></a>
### 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.

View file

@ -1,74 +0,0 @@
#!/bin/bash
# update "hooklet" to detect duplicate public keys
# This particular hooklet also serves as an example for people writing others.
# [It should be quite easy to figure out what parts apply to any hooklet and
# what parts are specific to *this* hooklet and its function.]
# see hooks/common/update.secondary.sample for instructions on *enabling*
# hooklets
# a hooklet is called as follows:
# git-receive-pack --> 'update' --> 'update.secondary' --> this script
# note: the same three arguments that git passes to the update hook are passed
# along to each hooklet.
# the update hook, and therefore the hooklets, are called for *every* repo out
# there. If you want this hooklet to run only for certain repos, here's how:
[ "$GL_REPO" = "gitolite-admin" ] || exit 0
# superfluous, since update.secondary already did it, but I'd like to
# emphasise that all output MUST go to STDERR
exec >&2
# ----
# the main functionality of the hooklet starts here. In this one (and I
# suspect many others) we want to examine the actual files from the commit
# that was pushed.
# get the tip commit being pushed
sha=$3
# git sets this; and we don't want it at this point...
unset GIT_DIR
# paranoia
set -e
# setup the temp area
export TMPDIR=$GL_REPO_BASE_ABS
export tmp=$(mktemp -d -t gl-internal-temp-repo.XXXXXXXXXX);
trap "rm -rf $tmp" EXIT;
# now get the files into $tmp.
# (note: if your task does not require the actual files, and you can
# manage with "git cat-file -s" and so on, then you may not even need a
# $tmp; you may be able to do it all right in the repo.git directory)
git archive $sha keydir | tar -C $tmp -xf -
# DO NOT try, say, 'GIT_WORK_TREE=$tmp git checkout $sha'. It'll screw up
# both the 'index' and 'HEAD' of the repo.git. Screwing up the index is
# BAD because now it goes out of sync with $GL_ADMINDIR. Think of a push
# that had a deleted pubkey but failed a hooklet for some reason. A
# subsequent push that fixes the error will now result in a $GL_ADMINDIR
# that still *has* that deleted pubkey!!
# And this is equally applicable to cases where you're using a
# post-receive or similar hook to live update a web site or something,
# which is a pretty common usage, I am given to understand.
cd $tmp
# ----
# *finally*, the actual check you need to do in this hook: look for duplicate
# pubkeys and exit 1 if dups are found
for f in `find keydir -name "*.pub"`
do
ssh-keygen -l -f "$f"
done | perl -ane '
die "$F[2] is a duplicate of $seen{$F[1]}\n" if $seen{$F[1]};
$seen{$F[1]} = $F[2];
'

View file

@ -1,69 +0,0 @@
#!/usr/bin/perl
# Technical notes:
# Gitolite specific script to check "author email" field of every commit
# pushed and to disallow if this email does not match the email that the
# user pushing is expected to have.
# Use without gitolite is also possible; just substitute your access
# control system's notion of "user" for the env var GL_USER in the code
# below and probably call it "update" if you dont already have an update
# hook.
# Mapping between "username" and "email address" is encapsulated in a
# subroutine for ease of changing; see code below.
# Philosophical notes:
# Doing this breaks the "D" in "DVCS", forcing all your developers to work
# to a centralised model as far as pushes are concerned. It prevents
# amending someone else's commit and pushing (this includes rebasing,
# cherry-picking, and so on, which are all impossible now). It also makes
# *any* off-line collabaration between two developers useless, because
# neither of them can push the result to the server.
# PHBs should note that validating the committer ID is NOT the same as
# reviewing the code and running QA/tests on it. If you're not
# reviewing/QA-ing the code, it's probably worthless anyway. Conversely,
# if you *are* going to review the code and run QA/tests anyway, then you
# don't really need to validate the author email!
# In a DVCS, if you *pushed* a series of commits, you have -- in some
# sense -- signed off on them. The most formal way to "sign" a series is
# to tack on and push a gpg-signed tag, although most people don't go that
# far. Gitolite's log files are designed to preserve that accountability
# to *some* extent, though; see contrib/adc/who-pushed for an admin
# defined command that quickly and easily tells you who *pushed* a
# particular commit.
# Anyway, the point is that the only purpose of this script is to
# - pander to someone who still has not grokked *D*VCS
# OR
# - tick off an item in some stupid PHB's checklist
use strict;
use warnings;
# mapping between gitolite userid and correct email address is encapsulated in
# this subroutine; change as you like
sub email_ok
{
my ($author_email) = shift;
my $expected_email = "$ENV{GL_USER}\@atc.tcs.com";
return $author_email eq $expected_email;
}
# print STDERR "SECONDARY HOOK:\n" . join(",", @ARGV, "\n");
my ($ref, $old, $new) = @ARGV;
for my $rev ( `git log --format="%ae\t%h\t%s" $new --not --all` ) {
chomp($rev);
my ($author_email, $hash, $subject) = split /\t/, $rev;
die "$ENV{GL_USER}, you can't push $hash authored by $author_email\n" .
"\t(subject of commit was $subject)\n"
unless email_ok($author_email);
}
exit 0;

View file

@ -1,11 +0,0 @@
## Vim Syntax Highlight
[Vim][] Syntax highlight for `gitolite.conf` can be found from:
- [vim.org script page][vim.org] (Releases)
- [GitHub][] (Sources)
[Vim]: http://www.vim.org/
[vim.org]: http://www.vim.org/scripts/script.php?script_id=2900
[GitHub]: http://github.com/tmatilai/gitolite.vim

127
convert-gitosis-conf Executable file
View file

@ -0,0 +1,127 @@
#!/usr/bin/perl -w
#
# migrate gitosis.conf to gitolite.conf format
#
# Based on gl-conf-convert by: Sitaram Chamarty
# Rewritten by: Behan Webster <behanw@websterwood.com>
#
use strict;
use warnings;
if (not @ARGV and -t or @ARGV and $ARGV[0] eq '-h') {
print "Usage:\n gl-conf-convert < gitosis.conf > gitolite.conf\n(please see the documentation for details)\n";
exit 1;
}
my @comments = ();
my $groupname;
my %groups;
my $reponame;
my %repos;
while (<>)
{
# not supported
if (/^repositories *=/ or /^map /) {
print STDERR "not supported: $_";
s/^/NOT SUPPORTED: /;
print;
next;
}
# normalise whitespace to help later regexes
chomp;
s/\s+/ /g;
s/ ?= ?/ = /;
s/^ //;
s/ $//;
if (/^\s*$/ and @comments > 1) {
@{$repos{$reponame}{comments}} = @comments if $reponame;
@{$groups{$groupname}{comments}} = @comments if $groupname;
@comments = ();
} elsif (/^\s*#/) {
push @comments, $_;
} elsif (/^\[repo\s+(.*?)\]$/) {
$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) {
push @{$repos{$reponame}{R}}, 'daemon';
} elsif (/^description\s*=\s*(.+?)$/) {
$repos{$reponame}{desc} = $1;
} elsif (/^owner\s*=\s*(.+?)$/) {
$repos{$reponame}{owner} = $1;
} elsif (/^\[group\s+(.*)\]$/) {
$reponame = '';
$groupname = $1;
} elsif (/^members\s*=\s*(.*)/) {
push @{$groups{$groupname}{users}}, map {s/\@([^.]+)$/_$1/g; $_} split(' ', $1);
} elsif (/^write?able\s*=\s*(.*)/) {
foreach my $repo (split(' ', $1)) {
$repo =~ s/\.git$//;
push @{$repos{$repo}{RW}}, "\@$groupname";
}
} elsif (/^readonly\s*=\s*(.*)/) {
foreach my $repo (split(' ', $1)) {
$repo =~ s/\.git$//;
push @{$repos{$repo}{R}}, "\@$groupname";
}
}
}
#use Data::Dumper;
#print Dumper(\%repos);
#print Dumper(\%groups);
# Groups
print "#\n# Groups\n#\n\n";
foreach my $grp (sort keys %groups) {
next unless @{$groups{$grp}{users}};
printf join("\n", @{$groups{$grp}{comments}})."\n" if $groups{$grp}{comments};
printf "\@%-19s = %s\n", $grp, join(' ', @{$groups{$grp}{users}});
}
# Gitweb
print "\n#\n# Gitweb\n#\n\n";
foreach my $repo (sort keys %repos) {
if ($repos{$repo}{desc}) {
@{$repos{$repo}{R}} = grep(!/^gitweb$/, @{$repos{$repo}{R}});
print $repo;
print " \"$repos{$repo}{owner}\"" if $repos{$repo}{owner};
print " = \"$repos{$repo}{desc}\"\n";
}
}
# Repos
print "\n#\n# Repos\n#\n";
foreach my $repo (sort keys %repos) {
print "\n";
printf join("\n", @{$repos{$repo}{comments}})."\n" if $repos{$repo}{comments};
#if ($repos{$repo}{desc}) {
# @{$repos{$repo}{R}} = grep(!/^gitweb$/, @{$repos{$repo}{R}});
#}
print "repo\t$repo\n";
foreach my $access (qw(RW+ RW R)) {
next unless $repos{$repo}{$access};
my @keys;
foreach my $key (@{$repos{$repo}{$access}}) {
if ($key =~ /^\@(.*)/) {
next unless defined $groups{$1} and @{$groups{$1}{users}};
}
push @keys, $key;
}
printf "\t$access\t= %s\n", join(' ', @keys) if @keys;
}
#if ($repos{$repo}{desc}) {
# print $repo;
# print " \"$repos{$repo}{owner}\"" if $repos{$repo}{owner};
# print " = \"$repos{$repo}{desc}\"\n";
#}
}

View file

@ -1,429 +0,0 @@
# gitolite installatation
In this document:
* <a href="#_please_read_this_first">please read this first</a>
* <a href="#_important_notes">important notes</a>
* <a href="#_requirements">requirements</a>
* <a href="#_client_workstation">client/workstation</a>
* <a href="#_server">server</a>
* <a href="#_technical_skills">technical skills</a>
* <a href="#_getting_the_gitolite_software">getting the gitolite software</a>
* <a href="#_getting_a_tar_file_from_a_clone">getting a tar file from a clone</a>
* <a href="#_installation_and_setup">installation and setup</a>
* <a href="#_install_methods_and_deciding_which_one_to_use">install methods and deciding which one to use</a>
* <a href="#_conventions_used">conventions used</a>
* <a href="#_package_method_directly_on_the_server_using_RPM_DEB">(package method) directly on the server, using RPM/DEB</a>
* <a href="#_root_method_directly_on_the_server_manually_with_root_access">(root method) directly on the server, manually, with root access</a>
* <a href="#_non_root_method_directly_on_the_server_manually_without_root_access">(non-root method) directly on the server, manually, without root access</a>
* <a href="#_from_client_method_install_from_the_client_to_the_server">(from-client method) install from the client to the server</a>
* <a href="#_URLs_for_gitolite_managed_repos">URLs for gitolite-managed repos</a>
* <a href="#_special_cases_multiple_gitolite_servers">special cases -- multiple gitolite servers</a>
* <a href="#_package_method_and_root_method">package method and root method</a>
* <a href="#_from_client_method">from-client method</a>
* <a href="#_upgrading">upgrading</a>
* <a href="#_uninstalling">uninstalling</a>
* <a href="#_cleaning_out_a_botched_install">cleaning out a botched install</a>
* <a href="#_uninstalling_gitolite_completely">uninstalling gitolite completely</a>
----
<a name="_please_read_this_first"></a>
### please read this first
<a name="_important_notes"></a>
#### important notes
Please make sure you understand the following points first.
* gitolite runs as a single (real) user on a server, and is invoked via ssh.
Traditionally, this "hosting user" is "git" or "gitolite".
* there is *usually* only one hosting user per server (machine), but
gitolite makes it trivial to have as many as you want. In fact, every
user on the server is a potential hosting user. Advanced users can
look [here][mgs]!)
* using this single user and sshd (or httpd) authentication, gitolite allows
you to create any number of "virtual" users. Virtual user names only mean
something to gitolite, and they need not be the same as any real userid on
the server or any of the clients accessing it.
* the first such virtual user is the "admin user", created during the
install sequence.
* gitolite, in its most commonly used form, depends **heavily** on ssh
pubkey (passwordless) access. Do not assume you know all about ssh --
most people **don't**. If in doubt, use a dedicated userid on both client
and server for installation and administration of gitolite.
To make matters worse, ssh problems in gitolite don't always look like ssh
problems. See [doc/ssh-troubleshooting.mkd][doc6] for help.
* gitolite **does NOT** like it when people with shell access to the server
fiddle with files and directories it controls.
Apparently this was not obvious to some people.
Thus a bare minimum gitolite setup has:
* a server
* a real "hosting user" on the server -- usually "git" or "gitolite"
* a virtual "admin user" -- the user who sets up gitolite and configures it.
* the admin user's client or workstation, from which he does all his work
It is possible to have the server and the client be the same machine, and even
the admin user be also the hosting user, (i.e., `sitaram@server` can install
and administer a gitolite setup running under `sitaram@server`, a situation
that is common with some hosting services). It's actually fairly easy and
**safe** to do, **as long as you have password access to the server** for
emergency use. However, I will not be documenting it because (a) if you know
ssh you'll know how to extrapolate my instructions to do this and (b) if you
don't know ssh it'll be a nightmare to support you.
<a name="_requirements"></a>
#### requirements
<a name="_client_workstation"></a>
##### client/workstation
* git version 1.6.2 or greater
* even msysgit on Windows is fine; please don't ask me for help if
you're using putty, plink, puttygen, etc., for ssh; I recommend
msysgit for Windows and the openssh that comes with it
* if you're using the "from-client" method of install (see below), the bash
shell is needed
* again, msysgit on Windows is fine
<a name="_server"></a>
##### server
* any Unix system with a posix compatible "sh".
* people using "csh" or derivatives please don't ask me for help -- tell
your admin csh is not posix compatible
* git version 1.6.2 or greater
* can be in a non-PATH location if you are unable to install it
normally; see the `$GIT_PATH` variable in the "rc" file
* perl (but since git requires it anyway, you probably have it)
* openssh or any ssh that can understand the `authorized_keys` file format
<a name="_technical_skills"></a>
##### technical 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.
<a name="_getting_the_gitolite_software"></a>
### getting the gitolite software
You can get the latest version of gitolite from github or indefero using the
'git clone' command:
git clone git://github.com/sitaramc/gitolite.git
# (OR)
git clone git://sitaramc.indefero.net/sitaramc/gitolite.git
<a name="_getting_a_tar_file_from_a_clone"></a>
#### getting a tar file from a clone
If you are on an internal network and cannot clone the gitolite repo, you can
do the clone on some other machine and create a tar file from it to use on the
internal network. Here's how:
git clone git://github.com/sitaramc/gitolite.git
# (OR)
git clone git://sitaramc.indefero.net/sitaramc/gitolite.git
cd gitolite
make master.tar
# or maybe "make pu.tar"
Please use the make command as shown, not a plain "git archive", because the
Makefile adds a file called `.GITOLITE-VERSION` that will help you identify
which version you are using.
<a name="_installation_and_setup"></a>
### installation and setup
<a name="methods"></a>
<a name="_install_methods_and_deciding_which_one_to_use"></a>
#### install methods and deciding which one to use
Gitolite has 4 install methods:
* **package method** if you have a gitolite RPM or a DEB available
* **root method** if you have root access to the server, and you plan to
have multiple "hosting users" on it
* **non-root method** if you don't have root access to the server, but you
do have at least one account with a password
* **from-client method** if you are not comfortable with public keys and
server side commands
Here's how you install using these 3 methods. Future upgrades are equally
easy -- the steps required for upgrading are marked "(U)".
<a name="_conventions_used"></a>
#### conventions used
Throughout the documentation, we use "YourName" as the admin user, and his
workstation is called "client". The hosting user is "git", and the server is
called "server". **Please substitute your values as needed**.
**If you're using DEB or RPM**, the installer creates a user called
"gitolite", so substitute that for "git" anywhere in the docs where the
"hosting user" is mentioned as "git".
Also, we often say "the rc file". This means `~/.gitolite.rc` on the server.
And when we say the "access control rules", or "conf file", or "config file",
we mean `conf/gitolite.conf` on your gitolite-admin clone.
<a name="_package_method_directly_on_the_server_using_RPM_DEB"></a>
#### (package method) directly on the server, using RPM/DEB
* from your workstation, copy your `~/.ssh/id_rsa.pub` file to the server.
Put it in `/tmp/YourName.pub`.
* (U) on the server, as root, do the install (urpmi, yum, apt-get, etc.).
* on the server, "su - gitolite", then as "gitolite" user, run `gl-setup
/tmp/YourName.pub`.
* on the client, run `cd; git clone gitolite@server:gitolite-admin`
<a name="_root_method_directly_on_the_server_manually_with_root_access"></a>
#### (root method) directly on the server, manually, with root access
* from your workstation, copy your `~/.ssh/id_rsa.pub` file to the server.
Put it in `/tmp/YourName.pub`.
* (U) on the server, as root, do the following:
cd $HOME
git clone git://github.com/sitaramc/gitolite gitolite-source
cd gitolite-source
# now checkout whatever branch you want; for early adopters I suggest
# "pu", as in "git checkout -t origin/pu" for recent gits
mkdir -p /usr/local/share/gitolite/conf /usr/local/share/gitolite/hooks
src/gl-system-install /usr/local/bin /usr/local/share/gitolite/conf /usr/local/share/gitolite/hooks
* on the server, as root, run "su - git", then as "git" user, run `which
gl-setup`. This should respond with `/usr/local/bin/gl-setup`. If this
is not what you get, you have some `$PATH` issues. Make sure
`/usr/local/bin` is in the `$PATH` for the git user, and that no prior
components of the path contain another copy of `gl-setup`. You *must* run
the one in the directory that is the first argument of `gl-system-install`
above.
* on the server, still as "git", run `gl-setup /tmp/YourName.pub`.
* on the client, run `cd; git clone git@server:gitolite-admin`
<a name="_non_root_method_directly_on_the_server_manually_without_root_access"></a>
#### (non-root method) directly on the server, manually, without root access
**WARNING: if you want to use this method your shell access to the git account
on the server should be via `su - git` from some other userid. If that is not
possible, you should (a) know the password in case you screw up the keys AND
(b) know how to force ssh to ask for a password if required, AND (c) know how
to handle multiple keys in ssh.**
* from your workstation, copy your `~/.ssh/id_rsa.pub` file to the server.
Put it in `/tmp/YourName.pub`.
* if `$HOME/bin` is not on the default PATH for the "git" user, fiddle with
the `.bashrc` or `.bash_profile` or similar files and add it somehow.
* (U) on the server, as "git", do the following:
cd $HOME
git clone git://github.com/sitaramc/gitolite gitolite-source
# now checkout whatever branch you want; for early adopters I suggest
# "pu", as in "git checkout -t origin/pu" for recent gits
cd gitolite-source
mkdir -p $HOME/bin $HOME/share/gitolite/conf $HOME/share/gitolite/hooks
src/gl-system-install $HOME/bin $HOME/share/gitolite/conf $HOME/share/gitolite/hooks
* on the server, still as "git", run `gl-setup /tmp/YourName.pub`.
* on the client, run `cd; git clone git@server:gitolite-admin`
<a name="fc"></a>
<a name="_from_client_method_install_from_the_client_to_the_server"></a>
#### (from-client method) install from the client to the server
The advantage of this method is that it forces you to solve the ssh pubkey
problem **before** attempting to install. It works best if you have dedicated
userids, one on the server for installing gitolite, and one on the client for
administering it.
The disadvantage is that the admin user ends up with [two keys][twokeys] --
one for shell access (that he started with) and one for gitolite access (which
the script creates if needed).
This in turn forces the admin to use a different URL to access gitolite repos
than normal users, which seems to confuse a heck of a lot of people who don't
read the prominently displayed messages and/or the documentation.
This method is verbosely documented in this [transcript][], including
*outputs* of the commands concerned.
<a name="_URLs_for_gitolite_managed_repos"></a>
### URLs for gitolite-managed repos
The URL for normal users (i.e., users other than the admin) is always of the
form "git@server:reponame". So, for instance, `git clone git@server:testing`
gets any valid user a copy of the "testing" repo.
In the first 3 install methods, the admin user will also use the same URL
format, like `git clone git@server:gitolite-admin`.
However, in the fourth ("from-client") method, the admin user needs a
different URL (`gitolite:reponame`) to gain access to the gitolite
repositories. Check [here][twokeys] for why.
<a name="_special_cases_multiple_gitolite_servers"></a>
### special cases -- multiple gitolite servers
(**Advanced users only, please!**)
There is no gitolite "daemon"; it gets invoked via sshd which calls
"gl-auth-command" via the "command=" option in the authkeys file (see
[gitolite and ssh][gash] for more).
If you think about it, this means every real (unix) user on the system can
host her own gitolite server!
Of course, one doesn't normally do that in the interests of sanity, but let's
say you want to create one gitolite instance for each department on some
company-wide mega-server.
<a name="_package_method_and_root_method"></a>
#### package method and root method
With the first two methods of installation, it's trivial to create multiple
gitolite instances. You can even do this without giving shell access to the
admins. Here's an example with just two "departments", and their admins Alice
and Bob:
* create userids `webbrowser_repos` and `webserver_repos`
* ask Alice and Bob for their pubkeys; copy them to the respective home
directories for convenience
* run `su - webbrowser_repos`, then `gl-setup alice.pub`
* (similarly with `webserver_repos` and `bob.pub`, and so on for others)
That's it. The URL for all web browser projects is now something like
`webbrowser_repos@server:reponame`, and similarly for the others.
Notice that you only have to do this once for each "department", and it's
really just one command after creating the userid. None of these admins need
to have a command line on the server, so don't give them the passwords if you
don't need to -- the pubkey will allow them to be gitolite admins on their
domain, and that's quite enough for normal operations.
<a name="_from_client_method"></a>
#### from-client method
You can do the same thing using this method also, but it's a little more
cumbersome. Instead, I'll describe a different scenario -- one "admin"
installing gitolite on different servers. You can adapt that to the above
case quite easily if you wish.
Thanks to Matt Perzel, the easy-install command now takes an optional 4th
parameter, which is the "nickname" of the gitolite server. It gets defined in
`~/.ssh/config`, and if not used it defaults to "gitolite".
So if you used the following command to install gitolite to 2 different
servers:
./src/gl-easy-install -q git my.1st.git.server admin_user1 gitolite_server_1
./src/gl-easy-install -q git my.2nd.git.server admin_user1 gitolite_server_2
you will find that `~/gitolite_server_1-admin` and `~/gitolite_server_2-admin`
have been created as respective clones. Or you can re-clone elsewhere:
cd ~/admin1; git clone gitolite_server_1:gitolite-admin.git
cd ~/admin2; git clone gitolite_server_2:gitolite-admin.git
<a name="_upgrading"></a>
### upgrading
Upgrading gitolite is easy. In each method above, just re-do the step that is
marked "(U)". Also, if you're using either of the two methods that use the
`src/gl-system-install` command, please make sure you give it the same
arguments!
If you've added any new hooks, please also run the next step (`gl-setup`)
also.
Also, remember that some new features may require additional settings in your
`~/.gitolite.rc` file.
<a name="_uninstalling"></a>
### uninstalling
<a name="_cleaning_out_a_botched_install"></a>
#### cleaning out a botched install
When people have trouble installing gitolite, they often try to change a bunch
of things manually on the server. This usually makes things worse ;-) so
here's how to clean the slate.
* client-side
* edit `~/.ssh/config` and delete the paragraph starting with `host
gitolite`, if present.
* remove `~/gitolite-admin`
* server-side
* edit `~/.ssh/authorized_keys` and delete all lines between `# gitolite
start` and `# gitolite end` inclusive.
* remove `~/.gitolite`, `~/.gitolite.rc` and
`~/repositories/gitolite-admin.git`
<a name="_uninstalling_gitolite_completely"></a>
#### uninstalling gitolite completely
There's some duplication between this and the previous section, but
uninstalling gitolite is described in great detail in
[doc/uninstall.mkd][doc9unin]
----
[doc6]: http://sitaramc.github.com/gitolite/doc/ssh-troubleshooting.html
[doc9unin]: http://sitaramc.github.com/gitolite/doc/uninstall.html
[twokeys]: http://sitaramc.github.com/gitolite/doc/ssh-troubleshooting.html#twokeys
[transcript]: http://sitaramc.github.com/gitolite/doc/install-transcript.html
[mgs]: http://sitaramc.github.com/gitolite/doc/1-INSTALL.html#_special_cases_multiple_gitolite_servers
[gash]: http://sitaramc.github.com/gitolite/doc/gitolite-and-ssh.html

View file

@ -1,383 +0,0 @@
# administering and running gitolite
In this document:
* <a href="#_please_read_this_first">please read this first</a>
* <a href="#_adding_users_and_repos">adding users and repos</a>
* <a href="#_using_hooks">using hooks</a>
* <a href="#_custom_hooks">custom hooks</a>
* <a href="#_gl_post_init_hook">"gl-post-init" hook</a>
* <a href="#_gl_pre_git_hook">"gl-pre-git" hook</a>
* <a href="#_hook_chaining">hook chaining</a>
* <a href="#_environment_variables_available_to_hooks">environment variables available to hooks</a>
* <a href="#_other_features">other features</a>
* <a href="#_moving_pre_existing_repos_into_gitolite">moving pre-existing repos into gitolite</a>
* <a href="#_moving_the_whole_thing_from_one_server_to_another">moving the whole thing from one server to another</a>
* <a href="#_specifying_gitweb_and_daemon_access">specifying gitweb and daemon access</a>
* <a href="#_custom_git_config">custom git config</a>
----
<a name="_please_read_this_first"></a>
### please read this first
Unless you know what you're doing, do not do **anything** manually on the
server (except when the documentation says you should, for example to add
custom hooks). In particular, adding new repositories or users or changing
the access control rules should not be done directly on the server. Things
will break. For example, if you manually create a repo on the server, it will
not have the required "update" hook, without which there is no access control
for pushes.
Most normal (day-to-day) gitolite admin work is done by cloning the
gitolite-admin repo from the server to your workstation, making changes to the
clone, and pushing those changes back.
The installation steps in the previous section include the steps to do this
clone, so you should already have one on your workstation, in
`~/gitolite-admin`. You can of course clone it anywhere else you want and use
that clone.
Either way, make sure you `cd` into this clone first.
*Note*: some of the paths in this document use variable names. Just refer to
`~/.gitolite.rc` for the correct values for *your* installation.
Once you've cloned it, you're ready to add users and repos.
<a name="_adding_users_and_repos"></a>
### adding users and repos
* ask each user who will get access to send you a public key. See other
sources (for example [here][genpub]) for how to do this
* rename each public key according to the user's name, with a `.pub`
extension, like `sitaram.pub` or `john-smith.pub`. You can also use
periods and underscores
* copy all these `*.pub` files to `keydir` in your gitolite-admin repo
clone. You can also organise them into various subdirectories of `keydir`
if you wish, since the entire tree is searched.
* edit the config file (`conf/gitolite.conf` in your admin repo clone). See
`doc/gitolite.conf.mkd` in the gitolite source for details on what goes in
that file, syntax, etc. Just add new repos as needed, and add new users
and give them permissions as required. The users names should be exactly
the same as their keyfile names, but without the `.pub` extension
* when done, commit your changes and push. Any new repos you specified will
automatically be created (empty, but clonable) and users' access will be
updated as needed.
<a name="_using_hooks"></a>
### using hooks
<a name="_custom_hooks"></a>
#### custom hooks
You can supply your own, custom, hook scripts if you wish. Install gitolite
as usual, then:
* if you installed using "from-client" method (gl-easy-install):
* go to the gitolite *source* clone from which you did the original
install
* add your new hook into "hooks/common"
* run src/gl-easy-install with the same arguments as you ran the first
time
* if you installed using one of the other methods
* go to ~/.gitolite/hooks/common on the server and put your new hook
there
* now run "gl-setup" again
You can use this procedure to install new hooks as well as to update hooks
that you had previously installed.
**VERY IMPORTANT SECURITY NOTE: the `update` hook in `hooks/common` is what
implements all the branch-level permissions in gitolite. If you fiddle with
the hooks directory, please make sure you do not mess with this file
accidentally, or all your fancy per-branch permissions will stop working.**
<a name="_gl_post_init_hook"></a>
#### "gl-post-init" hook
Sometimes it is necessary to do something whenever a new repo is created. If
you need this functionality, just supply a hook called "gl-post-init" with
whatever code you want in it.
<a name="_gl_pre_git_hook"></a>
#### "gl-pre-git" hook
Although git has lots of nice hooks you can tap into, they all run only on a
push. There's nothing that runs on a fetch or a clone, and there's no way to
run something *before* git-receive-pack or git-upload-pack, (as the case may
be) are invoked.
That's what the `gl-pre-git` hook is for. If an executable hook called
`gl-pre-git` is present, it will be invoked with the current directory set to
`repo.git`, and with a single argument which will be either `R` or `W`
depending on what the client is trying to do. The environment variables
`GL_USER` and `GL_REPO` are available. STDOUT will be forced to STDERR before
it is called, to avoid confusing the client.
If the code returns anything other than 0, gitolite will terminate the
operation (i.e., not run git at all), just like many git hooks do, so make
sure you end with `exit 0` or equivalent.
<a name="_hook_chaining"></a>
#### hook chaining
Gitolite basically takes over the update hook for all repos, but some setups
really need the update hook functionality for their own purposes too. In
order to allow this, Gitolite now exec's a hook called `update.secondary` when
it's own "update" hook is done and everything is ready to go.
You can create this `update.secondary` hook manually on selected repos on the
server, or use the mechanism in the previous section to make gitolite put it
on *all* your repos.
Similarly, gitolite also takes over the post-update hook for the special
"gitolite-admin" repo. This hook will also chain to a `post-update.secondary`
if such a hook exists. People wishing to do exotic things on the server side
when the admin repo is pushed should see doc/shell-games.notes for how to
exploit this :-)
In addition, gitolite now contains the basic infrastructure to support
multiple such hooks without having to remember to chain them yourself. You
can look in `hooks/common/update.secondary.sample` for instructions.
Finally, these names (`update.secondary` and `post-update.secondary`) are
merely the defaults. You can change them to anything you want; look in
conf/example.gitolite.rc for details.
<a name="_environment_variables_available_to_hooks"></a>
#### environment variables available to hooks
The following environment variables are set, and may be useful for any custom
processing you wish to do in your hook code:
* `GL_USER` -- the user doing the push
* `GL_REPO` -- the reponame
* `GL_REPO_BASE_ABS` -- the absolute base path where all the repos are kept
The following variables are also set, but are generally less useful:
* `GL_BINDIR` -- where all the binaries live
* `GL_ADMINDIR` -- common directory for many gitolite things
<a name="_other_features"></a>
### other features
<a name="_moving_pre_existing_repos_into_gitolite"></a>
#### moving pre-existing repos into gitolite
One simple way to add a pre-existing repo to gitolite is to let gitolite
create it as a brand new repo as in the previous section, then do the
following:
cd your-copy-of-the-repo
# make sure all the branches are correct and no extra stuff, "temp"
# branches, etc., are present
git push --all git@server:reponame
git push --tags git@server:reponame
(You could also use "git push --mirror" instead of separately doing branches
and tags, but that will carry across *your* remote refs also, and typically
you may not want that. Anyway please do a `git ls-remote git@server:repo` to
make sure all the stuff you want went through, and is named correctly).
All this is actually very simple and easily done. However, if you have many
existing repos to add, this can be time-consuming and error-prone. Here's how
to take a bunch of existing repos and add them to gitolite:
* make sure they're *bare* repos ;-)
* log on to the server and copy the repos to `$REPO_BASE` (which defaults to
`~/repositories`), making sure that the directory names end in ".git".
* back on your workstation, add each repo (without the `.git` suffix) to
`conf/gitolite.conf` in your gitolite-admin repo clone. Then add, commit,
push.
<a name="_moving_the_whole_thing_from_one_server_to_another"></a>
#### moving the whole thing from one server to another
[**NOTE**: I would appreciate help testing these instructions]
Just copying everything won't work unless everything on the new server is
exactly the same. I suggest you don't try it unless you know what you're
doing.
**Assumptions**
* you have not changed `$REPO_BASE` on either of the servers; if you did,
substitute accordingly
* the admin's name is "YourName" -- again, substitute accordingly!
* the "hosting user" on both servers is "git". Substitute whatever you're
actually using (for example, if you're installing using RPM/DEB, this
would be "gitolite")
There are many ways of doing this, but the most *generic* set of steps are
given below. Please follow all the steps; do not skip or improvise! Ask me
if things are not clear -- you can help me fine tune this document :-)
* (old server) **disable** the old server so your users will not push any
changes to it. There are several ways to do this, but the simplest is to
insert this line at the top of `~/.gitolite.rc` on the old server:
exit 1;
* (new server) **copy** the repos to the new server, **except** the
`gitolite-admin` repo and files called `gitolite-hooked` in the `hooks`
directory of each repo.
That sounds complicated but it's not. It's just:
cd $HOME
rsync -a olduser@oldhost:repositories .
mv repositories/gitolite-admin.git $HOME/old-gitolite-admin.git
find repositories -name gitolite-hooked | xargs rm
* (workstation) if your old server was installed using the "from-client"
method, and you intend to use the same method to install the new server,
then
* edit `~/.ssh/config` and change the line that says `host gitolite` to
`host old-gitolite`, or in fact anything that does not match the
string "host gitolite" :-)
* (workstation, new server) **install** gitolite normally on your new
server. Use whatever install method suits you, but you must use the
**same** name for the admin ("YourName" in the install instructions). You
may use a different keypair if you need to, or use the same one that
currently gets access to the old server.
* (new server) **edit** the `~/.gitolite.rc` file to match the settings on
the old server, if needed. Do not copy the entire file outright -- some
of the variables (notably `GL_PACKAGE_CONF` and `GL_PACKAGE_HOOKS`) are
installation dependent and should not be touched! Do a diff or a vimdiff
and copy across only what you know *you* changed on the old server.
* (workstation) **push** the config to the new server. To do this, go to
your admin clone, and:
* if you used a different keypair when installing to the new server,
copy that pubkey to this clone into `keydir/Yourname.pub`, then add
and commit the change to the pubkey
cd gitolite-admin
cp path/to/new/YourName.pub keydir/YourName.pub
git add keydir
git commit -m "new server, new key"
* if you did *not* use a different keypair, just make a dummy commit
git commit -m "new server" --allow-empty
* set the URL for the new server
git remote --set-url origin git@newserver:gitolite-admin
# if you used easy install this will be "gitolite:gitolite-admin"
* push the config, including past history
git push -f
And that should be that!
<a name="gwd"></a>
<a name="_specifying_gitweb_and_daemon_access"></a>
#### specifying gitweb and daemon access
This is a feature that I personally do not use (corporate environments don't
like unauthenticated access of any kind to any repo!), but someone wanted it,
so here goes.
Gitolite has two pre-defined, "special", usernames: `daemon` and `gitweb`.
To make a repo or repo group accessible via "git daemon", just give read
permission to the special user "daemon". Similarly, give read permission to
`gitweb` to allow the gitweb CGI to show the repo. Something like this:
repo foo bar baz
R = gitweb daemon
This gives you a quick way to offer multiple repos up for gitweb and/or daemon
access.
However, setting a description for the project also enables gitweb permissions
so you can do it that way if you want. Of course in this case you have to
deal with each repo separately. Add lines like this to gitolite.conf:
foo = "some description"
bar = "some other description"
baz = "yet another description"
You can also specify an owner for gitweb to show, if you like; for example I
might use:
gitolite "Sitaram Chamarty" = "fast, secure, fine-grained, access control for git"
Note that gitolite does **not** install or configure gitweb/git-daemon -- that
is a one-time setup you must do separately. All gitolite does is:
* for daemon, create the file `git-daemon-export-ok` in the repository
* for gitweb, add the repo (plus owner name, if given) to the list of
projects to be served by gitweb (see the config file variable
`$PROJECTS_LIST`, which should have the same value you specified for
`$projects_list` when setting up gitweb)
* put the description, if given, in `$repo/description`
The "compile" script will keep these files consistent with the config settings
-- this includes removing such settings/files if you remove "read" permissions
for the special usernames or remove the description line.
Please **note** that giving permissions to these special users via `@all`
(that is, using either `repo @all` or `R = @all`), will not work unless you
set the rc-file variable `$GL_ALL_INCLUDES_SPECIAL` to `1`. Also, **NOTE**
that giving them read access to `repo @all` means the `gitolite-admin` repo is
also accessible. **It is upto you to decide if that is OK in your
environment**.
<a name="_custom_git_config"></a>
#### custom git config
The custom hooks feature is a blunt instrument -- all repos get the hook you
specified and will run it. In order to make it a little more fine-grained,
you could set your hooks to only work if a certain "gitconfig" variable was
set. Which means we now need a way to specify "git config" settings on a per
repository basis.
[Note: this feature is disabled by default. Read the comments around a
variable called `GL_GITCONFIG_KEYS` in the rc file, then set it to some
appropriate value, to enable this feature.]
Thanks to Teemu (teemu dot matilainen at iki dot fi), gitolite now does this
very easily. For security reasons, this can only be done from the master
config file (i.e., if you're using delegation, the delegated admins cannot
specify git config settings).
Please see `doc/gitolite.conf.mkd` for syntax. Note that this only supports the
basic forms of the "git config" command:
git config section.key value # value may be an empty string
git config --unset-all section.key
It does not (currently) support other options like `--add`, the `value_regex`,
etc.
[genpub]: http://sitaramc.github.com/0-installing/2-access-gitolite.html#generating_a_public_key

View file

@ -1,534 +0,0 @@
# assorted faqs, tips, and notes on gitolite
In this document:
* <a href="#_common_errors_and_mistakes">common errors and mistakes</a>
* <a href="#_other_errors_warnings_notes_">other errors, warnings, notes...</a>
* <a href="#_cloning_an_empty_repo">cloning an empty repo</a>
* <a href="#_all_syntax_for_repos">`@all` syntax for repos</a>
* <a href="#_features">features</a>
* <a href="#_syntax_and_normal_usage">syntax and normal usage</a>
* <a href="#_one_user_many_keys">one user, many keys</a>
* <a href="#_security_access_control_and_auditing">security, access control, and auditing</a>
* <a href="#_two_levels_of_access_rights_checking">two levels of access rights checking</a>
* <a href="#_better_logging">better logging</a>
* <a href="#_delegating_parts_of_the_config_file">delegating parts of the config file</a>
* <a href="#_convenience_features">convenience features</a>
* <a href="#_what_repos_do_I_have_access_to_">what repos do I have access to?</a>
* <a href="#_support_for_git_installed_outside_default_PATH">support for git installed outside default PATH</a>
* <a href="#_personal_branches">"personal" branches</a>
* <a href="#_custom_hooks_and_custom_git_config">custom hooks and custom git config</a>
* <a href="#_bypassing_gitolite">bypassing gitolite</a>
* <a href="#_disabling_write_access_to_take_backups">disabling write access to take backups</a>
* <a href="#_INconvenience_features">INconvenience features</a>
* <a href="#_deleting_a_repo">deleting a repo</a>
* <a href="#_renaming_a_repo">renaming a repo</a>
* <a href="#_helping_with_gitweb">helping with gitweb</a>
* <a href="#_easier_to_link_gitweb_authorisation_with_gitolite">easier to link gitweb authorisation with gitolite</a>
* <a href="#_umask_setting">umask setting</a>
* <a href="#_advanced_features">advanced features</a>
* <a href="#_repos_named_with_wildcards">repos named with wildcards</a>
* <a href="#_admin_defined_commands">admin defined commands</a>
* <a href="#_access_control_for_external_commands">access control for external commands</a>
* <a href="#_svnserve">svnserve</a>
* <a href="#_design_choices">design choices</a>
* <a href="#_keeping_the_parser_and_the_access_control_separate">keeping the parser and the access control separate</a>
----
<a name="_common_errors_and_mistakes"></a>
### common errors and mistakes
* adding `repositories/` at the start of the repo name in the `git clone`.
This error is typically made by the *admin* himself -- because he knows
what `$REPO_BASE` is set to and thinks he has to provide that prefix on
the client side also :-) In fact gitolite prepends `$REPO_BASE`
internally, so you shouldn't also do the same thing!
* being able to clone but getting errors on push. Most likely caused by a
combination of:
* you already have shell access to the server, not just "gitolite"
access, *and*
* you cloned using `git clone git@server:repositories/repo.git` (notice
there's an extra "repositories/" in there?)
In other words, you used a key that completely bypassed gitolite and went
straight to the shell to do the clone.
Please see doc/ssh-troubleshooting.mkd for what all this means.
<a name="_other_errors_warnings_notes_"></a>
### other errors, warnings, notes...
<a name="_cloning_an_empty_repo"></a>
#### cloning an empty repo
Cloning an empty repo is only possible with clients greater than 1.6.2. So at
least one of your clients needs to have a recent git. Once at least one
commit has been made, older clients can also use it
When you clone an empty repo, git seems to complain about `fatal: The remote
end hung up unexpectedly`. However, you can ignore this, since it doesn't
seem to hurt anything. [Update 2009-09-14; this has been fixed in git
1.6.4.3]
<a name="_all_syntax_for_repos"></a>
#### `@all` syntax for repos
There *is* a way to use the `@all` syntax for repos also, as described in
`doc/gitolite.conf.mkd`. However, there are a couple of minor cautions:
* don't use `NAME/` or such restrictions on the special `@all` repo. Due to
the potential for defeating a crucial optimisation and slowing down *all*
access, we do not support this.
<a name="_features"></a>
### features
Apart from the big ones listed in the top level README, and subjective ones
like "better config file format", gitolite has evolved to have many useful
features than the original goal of branch-level access control.
<a name="_syntax_and_normal_usage"></a>
#### syntax and normal usage
<a name="multikeys"></a>
<a name="_one_user_many_keys"></a>
##### one user, many keys
I have a laptop and a desktop I need to access the server from. I have
different private keys on them, but as far as gitolite is concerned both of
them should be treated as "sitaram". How does this work?
The way it works is that you copy one pubkey as "sitaram@laptop.pub" and the
other as "sitaram@desktop.pub". The part before the "@" is the username, so
gitolite knows these two keys belong to the same person. The part after the
"@" can be anything you like, of course; gitolite doesn't care.
Note that you don't say "sitaram@laptop" and so on in the **config** file --
as far as the config file is concerned there's just **one** user called
"sitaram" -- so you only say "sitaram" there.
I think this is easier to maintain if you have to delete or change one of
those keys.
However, now that `sitaramc@gmail.com` is also a valid username, we need to
distinguish between `sitaramc@gmail.com.pub` and `sitaramc@desktop.pub`. We
do that by requiring that the multi-key suffix you use (like "desktop" and
"laptop") should not have a `"."` in it. If it does, it looks like an email
address. The following table lists sample pubkey filenames and the
corresponding derived usernames (which is what goes into the
`conf/gitolite.conf` file):
* old style multikeys; not mistaken for emails because there is no "." in
hostname part
sitaramc.pub sitaramc
sitaramc@laptop.pub sitaramc
sitaramc@desktop.pub sitaramc
* new style, email keys; there is a "." in hostname part; so it's an email
address
sitaramc@gmail.com.pub sitaramc@gmail.com
* multikeys *with* email address
sitaramc@gmail.com@laptop.pub sitaramc@gmail.com
sitaramc@gmail.com@desktop.pub sitaramc@gmail.com
<a name="_security_access_control_and_auditing"></a>
#### security, access control, and auditing
<a name="_two_levels_of_access_rights_checking"></a>
##### two levels of access rights checking
Gitolite has two levels of access checks. The **first check** is what I will
call the **pre-git** level. At this stage, the `gl-auth-command` has been
invoked by `sshd`, and it knows just three things:
* who,
* what repository, and
* what type of access (R or W)
Note that at this point no git program has entered the picture, and we have no
way of knowing what **ref** (branch, tag, etc) he is trying to update, even if
it is a "write" operation.
For a "read" operation to pass this check, the username (or `@all`) must have
read permission (i.e., R, RW, or RW+) on at least one branch of the repo.
For a "write" operation, there is an additional restriction: lines specifying
only `R` (read access) don't count. *The user must have write access to
**some** ref in the repo in order to pass this stage!*
The **second check** is via a git `update hook`. This check only happens for
write operations. By this time we know what "ref" he is trying to update, as
well as the old and the new SHAs of that ref (by which we can also deduce
whether it's a rewind or not). This is where the "per-branch" permissions
come into play.
Each refex that allows `W` access (or `+` if this is a rewind) for *this*
user, on *this* repo, is matched against the actual refname being updated. If
any of the refexes match, the push succeeds. If none of them match, it fails.
Gitolite also allows "exclude" or "deny" rules. See later in this document
for details.
<a name="_better_logging"></a>
##### better logging
If you have been too liberal with the permission to rewind, it has built-in
logging as an emergency fallback if someone goes too far, or for audit
purposes [`*`]. The logfile names and location are configurable, and can
include the year/month/day etc in the filename for easy archival or further
processing. The log file even tells you which pattern in the config file
matched to allow that specific access to proceed.
> [`*`] setting `core.logAllRefUpdates true` does provide a safety net
> against over-zealous rewinds, but it does not tell you "who". And
> strangely, management does not seem to share the view that "blame" is just
> a synonym for "annotate" ;-)]
The log lines look like this:
2009-09-19.10:24:37 + b4e76569659939 4fb16f2a88d8b5 myrepo refs/heads/master user2 refs/heads/master
The "+" at the start indicates a non-fast forward update, in this case from
b4e76569659939 to 4fb16f2a88d8b5. So b4e76569659939 is the one to restore!
Can it get easier?
The other parts of the log line are the name of the repo, the refname being
updated, the user updating it, and the refex pattern (from the config file)
that matched, in case you need to debug the config file itself.
<a name="_delegating_parts_of_the_config_file"></a>
##### delegating parts of the config file
You can now split up the config file and delegate the authority to specify
access control for their own pieces. See [delegation][] for details.
<a name="_convenience_features"></a>
#### convenience features
<a name="_what_repos_do_I_have_access_to_"></a>
##### what repos do I have access to?
Sometimes there are too many repos, maybe even named similarly, or with the
potential for typos, confusion about hyphens/underscores or upper/lower case,
etc. You'd just like a simple way to know what repos you have access to.
Gitolite provides two commands (`info` and `expand`) to help you find this
information; please check [doc/report-output.mkd][repout] for details.
<a name="_support_for_git_installed_outside_default_PATH"></a>
##### support for git installed outside default PATH
The normal solution is to add to the system default PATH somehow, either by
munging `/etc/profile` or by enabling `PermitUserEnvironment` in
`/etc/ssh/sshd_config` and then setting the PATH in `~/.ssh/.environment`.
All these are security risks because they allow a lot more than just you and
your git install :-)
And if you don't have root, you can't do this anyway.
The only solution till now has been to ask every client to set the config
parameters `remote.<name>.receivepack` and `remote.<name>.uploadpack`. But
telling *every* client to do so is a pain...
Gitolite lets you specify the directory in which git binaries are to be found,
via a new variable (`$GIT_PATH`) in the "rc" file. If this variable is
non-empty, it will be appended to the PATH environment variable before
attempting to run git stuff.
Very easy, very simple, and completely transparent to the users :-)
**Note**: sometimes you have a system that already has an older "git"
installed in one of the system PATHs, but you've installed a newer git in some
non-standard location and want that picked up. Because of security reasons,
gitolite will not prepend `GIT_PATH` to the PATH variable, so the older git
comes first and it gets kinda frustrating!
Here's a simple workaround. Ignore the `GIT_PATH` variable, and directly set
the full PATH in the rc file, like so:
$ENV{PATH} = "/home/sitaram/bin:$ENV{PATH}";
<a name="_personal_branches"></a>
##### "personal" branches
"personal" branches are great for corporate environments, where
unauthenticated pull/clone is a no-no. Since a dev workstation cannot do
authentication, even work shared just between 2 devs has to go *via* the
server. This causes the same branch name clutter as in a centralised VCS,
plus setting up permissions for this becomes a chore for the admin.
gitolite lets you define a "personal" or "scratch" namespace prefix for each
developer (e.g., `refs/personal/<devname>/*`). Just add a line like:
RW+ personal/USER/ = @userlist
This means I (user "sitaram") can do anything to any branch whose name starts
with `personal/sitaram/` assuming I'm in "userlist".
You can have any number of such lines with different prefixes (for example,
using topic names instead of "personal") or even suffixes if you like. The
important thing is that the "branch" name should contain `/USER/` (including
the slashes). At runtime this will match whoever is the current user. Access
is still determined by the right hand side of course.
<a name="_custom_hooks_and_custom_git_config"></a>
##### custom hooks and custom git config
You can specify hooks that you want to propagate to all repos, as well as
per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and
`doc/gitolite.conf.mkd` for details.
<a name="_bypassing_gitolite"></a>
##### bypassing gitolite
Sometimes you'll need to access one of the gitolite-managed repos directly on
the server, without going through gitolite. Reasons may be some automatic
updates or some other ad hoc purposes you can dream up.
Cloning a gitolite-controlled repo is easy enough -- just use the full path
(typically `~/repositories/reponame.git`) instead of just `reponame`, to
compensate for gitolite not sitting in between and adding those things to the
repo path.
But when you push, the update hook (which git will invoke anyway) will fail
because it needs all sorts of access control info that it now doesn't have,
because the push was invoked without going through gitolite.
In order to bypass the update hook, just set the `GL_BYPASS_UPDATE_HOOK`
environment variable to "1" or something, export it, and push. I prefer not
to set that variable permanently, preferring this mode instead:
GL_BYPASS_UPDATE_HOOK=1 git push
**WARNING**: Do **NOT** try this with the special `gitolite-admin` repo. That
repo also runs a `post-update` hook which needs additional information which
is NOT available if you bypass gitolite. Mucking with that repo in this
manner is strongly discouraged, as in "are you feeling lucky today?". Use
`gl-dont-panic` if you need to do some server-side surgery for that repo.
<a name="_disabling_write_access_to_take_backups"></a>
##### disabling write access to take backups
If you want to take normal, OS-level, backups of the system, you might want
git to be quiescent during that time, so that the backup is clean. The best
way to do this is to disable write-access to the server for the duration of
the backup.
Here's how:
cd $HOME # if running as "git" user, else "cd ~git" or whatever
echo writes disabled during backup window > .gitolite.down
# << RUN YOUR BACKUP COMMAND(s) HERE >>
rm .gitolite.down
I leave it to you to
* make sure that if the backup script fails, the `.gitolite.down` file is
still removed (or not; maybe your policy is that if the backup failed, no
further writes are allowed. Whatever...)
* if you're extremely paranoid (even I wouldn't worry about this!) make sure
that no push is *in progress* by checking for any `git-receive-pack`
processes in a `ps` output.
<a name="_INconvenience_features"></a>
#### INconvenience features
<a name="_deleting_a_repo"></a>
##### deleting a repo
By design, there is no code in gitolite to *delete* a repo if the repo was
specified by name in the config file. (Wildcard repos *can* be deleted by the
user; see [here][rmrepo] for details).
If you *do* want to permanently delete a *non*-wildcard repo, here's what you
do:
* remove the repo from the gitolite-admin repo clone's `conf/gitolite.conf`
file. "add" the change, commit, and push.
* *then* remove the repo from `~/repositories` on the server (or whatever
you set `$GL_REPO_BASE` to in the `~/.gitolite.rc`)
<a name="_renaming_a_repo"></a>
##### renaming a repo
This is similar; there's no code to do this in gitolite. What you do is:
* log on to the server, `cd $REPO_BASE` (default: `cd ~/repositories`), and
`mv old-name.git new-name.git`
* back on your gitolite-admin clone, edit `conf/gitolite.conf` and replace
all occurrences of `old-name` with `new-name`. Then add, commit, and push
as usual.
The order of these 2 steps is important; do not reverse them :-)
<a name="_helping_with_gitweb"></a>
#### helping with gitweb
Although gitweb is a completely separate program, gitolite can do quite a
lot to help you manage gitweb access as well; once the initial setup is
complete, you can do it all from within the gitolite config file!
If you just want gitweb to show some repositories, see [gwd] for how to
specify which repos to show. Other advanced uses are described here.
[gwd]: http://sitaramc.github.com/gitolite/doc/2-admin.html#gwd
<a name="_easier_to_link_gitweb_authorisation_with_gitolite"></a>
##### easier to link gitweb authorisation with gitolite
Over and above whether a repo is even *shown* by gitweb, you may want to
further restrict people, allowing them to view *only* those repos for which
they have been given read access by gitolite.
This requires that:
* you have to have some sort of HTTP auth on your web server (out of my
scope, sorry!)
* the HTTP auth should use the same username (like "sitaram") as used in the
gitolite config (for the corresponding user)
Normally a superuser sets up passwords for users using the "htpasswd" command,
but this is an administrative chore.
Robin Smidsrød had the *great* idea that, since each user already has pubkey
access to `git@server`, this gives us a very neat way of using gitolite to let
the users *manage their own HTTP passwords*. Here's how:
* setup apache so that the htaccess file it looks for is owned by the "git"
user
* in the `~/.gitolite.rc` file, look for the variable `$HTPASSWD_FILE` and
point it to this file
* tell your users to type in `ssh git@server htpasswd` to set or change
their HTTP passwords
Of course some other authentication method can be used (e.g. `mod_ldap`) as
long as the usernames match.
Gitweb allows you to specify a subroutine to decide on access. We use that
feature and tie it to gitolite. Configuration example can be found in
`contrib/gitweb/`.
<a name="_umask_setting"></a>
##### umask setting
Gitweb not able to read your repos? You can change the umask for newly
created repos to something more relaxed -- see the `~/.gitolite.rc` file
<a name="_advanced_features"></a>
#### advanced features
<a name="_repos_named_with_wildcards"></a>
##### repos named with wildcards
Please see `doc/wildcard-repositories.mkd` for all the details.
<a name="_admin_defined_commands"></a>
##### admin defined commands
This requires the wildcards feature to be enabled, but is then an extremely
powerful feature. See `doc/admin-defined-commands.mkd`.
<a name="_access_control_for_external_commands"></a>
##### access control for external commands
Gitolite now has a mechanism for allowing access control for arbitrary
external commands, as long as they are invoked via ssh and present a
server-side command that contains enough information to make an access control
decision.
Note that this is incompatible with giving people shell access as described in
`doc/ssh-troubleshooting.mkd` -- people who have shell access are not
subject to this mechanism (it wouldn't make sense to try and control someone
who has shell access anyway).
In general, external commands require changes in one or both the config files;
the sample files in `conf/` double as documentation, so you should look there
for examples and usage.
Commands implemented so far are:
* rsync
* svnserve (see next section for a brief description; this has been
contributed by Simon and Vladimir)
<a name="_svnserve"></a>
###### svnserve
If you are transitioning from SVN to gitolite, and have a lot of users using
public-key authentication with SVN, this feature may be useful to you. Once
you migrate all users' public keys into gitolite, you can set the `$SVNSERVE`
variable in `~/.gitolite.rc` to tie `svnserve` with gitolite's authentication
system. Assuming you installed gitolite to the same user as the one you used
for SVN, SVN connectivity will be retained, and users will be able to use
both SVN and git using the same SSH configuration.
<a name="_design_choices"></a>
### design choices
<a name="_keeping_the_parser_and_the_access_control_separate"></a>
#### keeping the parser and the access control separate
There are two programs concerned with access control:
* `gl-auth-command`, the program that is run via `~/.ssh/authorized_keys`;
this decides whether git should even be allowed to run (basic R/W/no
access). (This one cannot decide on the branch-level access; it is not
known at this point what branch is being accessed)
* the update-hook on each repo, which decides the per-branch permissions
I have chosen to keep the relatively complex task of parsing the config file
out of them to keep them simpler (and faster). So any changes to the config
have to be first "compiled", and the access control programs use this
"compiled" version of the config. (The compile step also refreshes
`~/.ssh/authorized_keys`).
If you choose the "easy install" method, all this is quite transparent to you
anyway. If you cannot use the easy install and must install manually, I have
clear instructions on how to set it up.
[repout]: http://sitaramc.github.com/gitolite/doc/report-output.html
[delegation]: http://sitaramc.github.com/gitolite/doc/delegation.html
[rmrepo]: http://sitaramc.github.com/gitolite/doc/admin-defined-commands.html#rmrepo

View file

@ -1,241 +0,0 @@
Major changes to gitolite, master branch only, most recent first, no dates but
the tags can help you position stuff approximately
[NYD = not yet documented due to lack of time...]
- v2.0.1
- "hooklets" allow you to have any number of site-local 'update hook'
scripts play nicely with gitolite
- the "include" statement can now use globs -- makes some types of setups
sooooo easy!
- mirroring finally tested properly after v2
- 'hub' adc (pull requests etc)
- warn() and die() are now logged
- v2.0
- gl-pre-git hook added
- 'hub' adc added
- v2.0rc2
*** V1.5.9.1 -- IMPORTANT SECURITY FIX; PLEASE UPGRADE IF YOU'RE USING
SOMETHING OLDER THAN THIS ONE
- supercool new 'git' adc; so cool it's disabled by default
- '-prune' added to find commands; makes a big diff if REPO_BASE is NFS/CIFS
mounted
- first python ADC contributed -- 'perms' makes setperms etc easier to do
- 'set-head' ADC added
- v2.0rc1; major refactor
- v1.5.9
- Nokia MeeGo team contributed ldap scripts
- large configs should now be twice as fast (except when gl-perms exists)
due to my finding and eliminating a wasted parse_acl
- major change: split the config file when in big-config mode
(includes a data format change)
- GL_ALL_READ_ALL to make things much, (MUCH!) faster for sites where all
can read all repos (like Fedora)
- rc file revamp -- was getting too big and unwieldy; now the documentation
is in a new file instead of inline
- allow gitolite to be used even when users have real IDs (thus $HOME is not
valid to find the rc file); allow /etc/gitolite/gitolite.rc then
- BIG one for adc writers -- full blown access checks (ref level) can be
done from an ADC now (though it has to be in perl, not shell)!
- allow full access checks from perl (shell can only do level 1 checks);
useful in hooks or ADCs
- v1.5.8
- Jeff from KDE contributed the watch ADC
- allow disabling gitolite (write access only) to take backups
- document how to move gitolite from one server to another
- custom permissions categories (default being READERS and WRITERS) for
setperms (thanks to Jeff from KDE for the idea/need)
- v1.5.7
- "help" adc can allow site local help text to be shown
- "who-pushed" adc to find out who pushed a given commit
- rmrepo adc replaced by lock/unlock/rm and trash/list-trash/restore
families of commands
- v1.5.6
- new method for passing usergroup info (minor backward compat breakage);
see commit message and doc/big-config.mkd for more on this
- added "sudo" (adc)
- added "gl-reflog" (adc) to get a fake reflog from the server!
- added support for a post-repo-create hook called "gl-post-init"
- (BIG ONE!) SMART HTTP SUPPORT!
- @all can now include gitweb/daemon also (default, not include)
- allow @groups in setperms
- gitweb/daemon now work within setperms
- log elapsed time (optional)
- more than one wildcard may match a repo, plus it can also be matched by a
normal repo line
- test suite has lots of new tests for the below
- (big change) all combinations of wild repos and big configs, including
daemon/gitweb/git-config settings, should work now!
- v1.5.5
- mirroring support
- setup_authkeys is now separate; can be called from outside also; useful
for people who want to maintain ssh keys via LDAP or something, and not
within gitolite
- (two months too late for towel day) gl-dont-panic!
[replaces the old "gl-emergency-addkey" program. It does more (including
recovering from a botched push, not just lost keys), is cleaner, and works
for all install methods]
- document on how to create a mob branch
- info command now takes a parameter to limit output; this is mandatory if
GL_BIG_CONFIG is on
- v1.5.4
- new RC variables: GL_NO_CREATE_REPOS and GL_NO_SETUP_AUTHKEYS (inspired by
the specific needs that Fedora have, but made as generic as possible)
- separating push branch rights from create branch rights changed to use the
same mechanism as the (older) mechanism for separating rewind from delete
- v1.5.3
- log file format changed; minor backward compat breakage if you've been
doing any automated log processing
- some small but important doc updates
- adc "fork" now much faster and more space-efficient (uses git clone -l)
- v1.5.2
- added test suite
- v1.5.1
- disallow creation of new refs if you want (while allowing push of the
same)
- adc "able" command added to contrib
- easy-install now takes a host-nickname parameter for convenience in
installing more than one gitolite server
- major doc revamp; contrib/autotoc added to make docs look nicer
- eliminate the need to run gl-setup on data version change, thus hopefully
obsoleting the upgrade note for v1.5 (just below).
- v1.5 -- IMPORTANT UPGRADE NOTES below
Upgrading to v1.5 from any version prior to v1.5 requires an extra step
for people who installed gitolite using the "system install / user setup"
method described in doc/0-INSTALL.mkd. For such installations, after the
administrator has upgraded gitolite system-wide, each "gitolite host" user
must run `gl-setup` once (this time without any arguments).
- "deny" rules should now work even in "big-config" due to previous change
- proper rule sequencing (required major format change)
- allow usergroup info to be passed in from outside, say via LDAP; see
doc/big-config.mkd for details
- (new) big-config is now part of mainline (old one had bitrotted); see
doc/big-config.mkd for details
- gl-system-install: help people simulate an RPM/DEB install by just running
that commmand with appropriate arguments; see doc/0-INSTALL.mkd
- admin-defined commands; see doc/admin-defined-commands.mkd
- v1.4.2 (prep for major refactor on rights queries
- v1.4.1 (security fix)
- REFUSE TO RUN ON SERVER GIT < 1.6.2 (do NOT upgrade gitolite to or beyond
this point if you are unable to upgrade git itself to at least 1.6.2)
- "D" must be combined with RW or RW+ (warning: minor backward compat breakage)
- v1.4
- recurse through keydir for pubkeys
- bypass update hook if GL_BYPASS_UPDATE_HOOK is available in ENV
- new server-side program "gl-tool", subcommand "shell-add"
- new "D" permission (makes RW+ no longer imply "D" if used)
- @all for repos is now a true @all
- allow setperms to specify @all
- post-update hook and gl-setup should be dash compat now
- workaround for a Data::Dumper crash; see 412a691
- both hooks chain to "<hookname>.secondary" now
- new style personal branches (see 2456cc1 for advantages)
- v1.3
- easier to move repos into gitolite
- pattern for expand is no longer anchored
- v1.2
- distro packaging support -- easy to install systemwide now
- v1.1
- contrib directory added
- expand now lists non-wildcard repos also
- refs also have groups now
- allow admins to get "info" for other users
- wildrepos merged
- getdesc and setdesc for wildrepos
- htpasswd subcommand
- access control for rsync
- v1.0
- sshkeys-lint program added, doc/6 revamped
- @SHELL in config changed to $SHELL_USERS in rc
- "include" mechanism
- delegation now uses NAME/ instead of branches
- PATH/ changed to NAME/
- @SHELL in config
- use of @all for repos also (see doc for caveat)
- config entries for repos
- deny rules (no more "rebel" branch!)
- PATH/
- specify gitweb owner
- v0.95
- easy install can run from msysgit also
- v0.90
- allow admin defined hooks
- specify gitweb desc
- v0.85
- emergency addkey program
- v0.80

View file

@ -1,227 +0,0 @@
## admin defined commands
**WARNING: Use this feature only if you really really know what you're doing.
If you come back to me saying this feature caused a problem, I will only
laugh. The worse the problem, the louder I will laugh. You won't actually
hear me laughing, but you'll feel it in your bones, trust me!**
There may be other such **WARNING** sections below. **Read all of them**.
----
In this document:
* <a href="#_background">background</a>
* <a href="#_setting_it_up">setting it up</a>
* <a href="#_anatomy_of_a_command">anatomy of a command</a>
* <a href="#_example_uses_and_sample_commands_in_contrib">example uses and sample commands in contrib</a>
* <a href="#_fork">fork</a>
* <a href="#_deleting_trashing_repos">deleting/trashing repos</a>
* <a href="#_enable_disable_push_access_temporarily">enable/disable push access temporarily</a>
* <a href="#_bonus_restricted_admin">(bonus) restricted admin</a>
----
<a name="_background"></a>
### background
Gitolite was named to be short for "gitosis-lite". Someone now wants to turn
it into a "github-lite" :-) and even had some code to start me off thinking.
Since my first impulse on being asked for a feature is to say no, I was
casting about for a reason when he gave me one: he first made some noises
about perl, then said something about rewriting it all in scheme. Nice... I
resisted the urge to point him to [this][xkcd224], told him that's a great
idea and he should go for it, mentally blessing him for letting me off the
hook on coding it ;-) [Laziness][lazy] *is* the first virtue you know!
And that was that. For a couple of days.
Soon, though, I realised that there could be a pretty big bonus in this for
tightly controlled setups, so I went and coded it all anyway. See the section
on "restricted admin" for what's really exciting about this for *me*.
----
It may be a good idea to read [doc/wildcard-repositories.mkd][wild] before
you continue here though, because most of the uses of this feature also need
wildcard repos. (This also means you must set `$GL_WILDREPOS` to "1" in the
rc file).
The wildcard repo feature is a way to create repositories matching a pattern
(even if it as simple as `personal/CREATOR/.+`), and a way to specify two
categories of permissions for each such user-created repo.
What we want now is more than that, as you'll see in the examples below. And
I'm sure if you think of more uses you'll send them to me as "contrib"
entries, right?
<a name="_setting_it_up"></a>
### setting it up
This can only be setup by someone who has shell access to the server. Edit
the rc file and update the `$GL_ADC_PATH` variable to point to, say,
`/home/git/bin/adc`. *Nothing happens unless this variable is set and
pointing to a directory*. Then put in whatever such commands you create into
that directory. If you have a command called "foo" in that directory, then a
user can invoke it by saying:
ssh git@server foo argument list
**WARNING: When gitolite takes control, this directory is checked first, and
if the requested command exists, it is executed. It is therefore quite easy
to inadvertently *hide* some of the "official" commands (like "info",
"expand", "setperms", etc., or worse, say "git-upload-pack"!) by creating
executable files with those names in this directory. So don't do that -- you
have been warned!**
#### configuring ADCs
I didn't want to put configuration variables for ADCs also into the main 'rc'
file, so I chose to put them in the `adc.common-functions` file instead. Take
a look at it sometime.
<a name="_anatomy_of_a_command"></a>
### anatomy of a command
You can basically do whatever you want in such a command -- go wild! It's
upto you to check the permissions of *each* repo that the user is manipulating
using your command -- you can `rm -rf $GL_REPO_BASE_ABS` if you like and
gitolite wouldn't stop you.
The current directory (`$PWD`) will be set to the `$HOME` of `git@server` (or
whatever id you're using). It won't be any specific repo, it won't even be
the base directory of all the repos.
Gitolite defines a few environment variables, as well as allows you to
directly query the ownership and access permissions of any repository.
The environment variables available are:
* `GL_USER` -- the name of the user invoking the command
* `GL_BINDIR` -- the directory containing all the binaries (in particular,
`gitolite.pm`, which is all we really care about here)
* `GL_REPO_BASE_ABS` -- the absolute path of the base directory containing
all the repos
There are a few other variables also available but the above are the only ones
you should rely on. Please treat any other variables you notice as being
internal/undocumented/subject to change.
[Implementation note: some of the distro packagers don't seem to like
`GL_BINDIR`. I have not tested this in those scenarios, but they probably put
`gitolite.pm` somewhere in perl's lib path anyway, so it ought to work].
In addition, all the arguments of the command are also available to you, so
you can define your own command syntaxes. Gitolite checks these arguments to
make sure they fit a very conservative pattern (see `$ADC_CMD_ARGS_PATT` in
`src/gitolite.pm`), so take that into consideration when designing your
commands and usage.
Finally, you can call gitolite to query ownership and permissions for the
current user (which may not necessarily be the owner). This is done loosely
as follows (don't use this exact code yet though):
perl -I$GL_BINDIR -Mgitolite -e "cli_repo_rights('reponame')"
which will print two space-separated words, something like `_____R__W u1` or
maybe `____@R_@W <gitolite>`. (The former is the response for a wildcard repo
created by user `u1`, the latter is for a non-wildcard repo)
But that's cumbersome. There's a bash shell function called
`get_rights_and_owner` in `contrib/adc/adc.common-functions` that is much more
convenient. See any of the other samples for how to use it.
If you don't like this, roll your own. If you don't like bash, do the eqvt in
your language of choice.
<a name="_example_uses_and_sample_commands_in_contrib"></a>
### example uses and sample commands in contrib
<a name="_fork"></a>
#### fork
A user would use the fork command like this:
ssh git@server fork from to
where "from" is a repo to which the user invoking the fork has "R" access, and
"to" is a repo that does not yet exist and to which he has "C" access.
(Reminder: these access checks are done by the "fork" script, **not** within
gitolite -- once again, you are responsible for making sure your scripts
maintain the security of the system!)
Strictly speaking this command is not really needed. Even without all this
"admin-defined commands" setup you could still do the following, purely from
the client side:
git clone git@server:from
cd from
git remote add new git@server:to
git push new refs/*:refs/*
or some such incantation.
<a name="rmrepo"></a>
<a name="_deleting_trashing_repos"></a>
#### deleting/trashing repos
See [contrib/adc/repo-deletion.README][rdR] for details about this.
[rdR]: http://github.com/sitaramc/gitolite/blob/pu/contrib/adc/repo-deletion.README
<a name="_enable_disable_push_access_temporarily"></a>
#### enable/disable push access temporarily
If you want to disable push access to gitolite temporarily (maybe for
maintenance), anyone with write access to the gitolite-admin repo can do this:
ssh git@server able dis @all # able dis ==> dis able
To re-enable after the maint work is done:
ssh git@server able en @all # able en ==> en able
You can also do this for one or more individual repos; in place of `@all`,
just use a space separated list of reponames (exactly as they would appear in
the config file). Wildcards are not supported; patches welcome ;-)
Note: please see [this][diswr] for more on this.
[diswr]: http://sitaramc.github.com/gitolite/doc/3-faq-tips-etc.html#_disabling_write_access_to_take_backups
<a name="_bonus_restricted_admin"></a>
#### (bonus) restricted admin
It's rather important to me (and presumably others in the "corporate" world)
to separate permission to push to the "gitolite-admin" repo from unrestricted
shell access to the server. This issue has been visited often in the past.
Until now, though, this was binary -- you either had full shell access or none
at all. If there were tasks that legitimately needed to be done from the
shell on the server, it often meant you had to break that separation or load
the few people who did have shell access already.
Now, however, it is possible to provide scripts to do what you want, and put
them in `$GL_ADC_PATH`. `contrib/adc/restrict-admin` is a commented sample --
as you can see, it cleverly makes use of the fact that you can now check for
the invoking uses access to any repo in the system. In this case it checks if
he has "W" access to the gitolite-admin repo, and if he does, allows the
script to proceed.
[Note that this particular use does not require `$GL_WILDREPOS` to be enabled,
because it's not using any wildcard repos].
[xkcd224]: http://xkcd.com/224/
[lazy]: http://c2.com/cgi/wiki?LazinessImpatienceHubris
[wild]: http://sitaramc.github.com/gitolite/doc/wildcard-repositories.html

View file

@ -1,430 +0,0 @@
## what is a "big-config"
In this document:
* <a href="#_when_why_do_we_need_it_">when/why do we need it?</a>
* <a href="#_how_do_we_use_it_">how do we use it?</a>
* <a href="#_access_rules_for_groups">access rules for groups</a>
* <a href="#_access_rules_for_individual_repos_split_config_">access rules for individual repos (split config)</a>
* <a href="#_other_optimisations">other optimisations</a>
* <a href="#_disabling_various_defaults">disabling various defaults</a>
* <a href="#_optimising_the_authkeys_file">optimising the authkeys file</a>
* <a href="#_what_are_the_downsides_">what are the downsides?</a>
* <a href="#_storing_usergroup_information_outside_gitolite_like_in_LDAP_">storing usergroup information outside gitolite (like in LDAP)</a>
* <a href="#_why">why</a>
* <a href="#_how">how</a>
* <a href="#_implementation_notes">implementation notes</a>
<a name="_when_why_do_we_need_it_"></a>
### when/why do we need it?
A "big config" is anything that has a few thousand users and a few thousand
repos, resulting in a very large 'compiled' config file.
To understand the problem, consider what happens if you have something like
this in your gitolite conf file:
@wbr = lynx firefox
@devs = alice bob
repo @wbr
RW+ next = @devs
RW master = @devs
Without the 'big config' setting, gitolite internally translates this to:
repo lynx firefox
RW+ next = alice bob
RW master = alice bob
and then generates the actual config rules once for each user-repo-ref
combination (there are 8 combinations above); the compiled config file looks
somewhat like this:
%repos = (
'firefox' => {
'R' => {
'alice' => 1,
'bob' => 1
},
'W' => {
'alice' => 1,
'bob' => 1
},
'alice' => [
[
0,
'refs/heads/next',
'RW+'
],
[
4,
'refs/heads/master',
'RW'
]
],
'bob' => [
[
1,
'refs/heads/next',
'RW+'
],
[
5,
'refs/heads/master',
'RW'
]
]
},
'lynx' => {
'R' => {
'alice' => 1,
'bob' => 1
},
'W' => {
'alice' => 1,
'bob' => 1
},
'alice' => [
[
2,
'refs/heads/next',
'RW+'
],
[
6,
'refs/heads/master',
'RW'
]
],
'bob' => [
[
3,
'refs/heads/next',
'RW+'
],
[
7,
'refs/heads/master',
'RW'
]
]
}
);
Phew!
Of course, the output is the same whether you used groups (like `@wbr` and
`@devs` in the example above) or listed the repos directly on the 'repo'
lines.
Anyway, you can imagine what that does when you have 10,000 users and 10,000
repos. Let's just say it's not pretty :)
<a name="_how_do_we_use_it_"></a>
### how do we use it?
Just set
$GL_BIG_CONFIG = 1;
in the `~/.gitolite.rc` file on the server (see next section for more
variables). When you do that, and push this configuration, one of two things
happens.
<a name="_access_rules_for_groups"></a>
#### access rules for groups
If you used group names in the 'repo' lines (as in `repo @wbr`), then the
compiled config looks like this:
%repos = (
'@wbr' => {
'@devs' => [
[
0,
'refs/heads/next',
'RW+'
],
[
1,
'refs/heads/master',
'RW'
]
],
'R' => {
'@devs' => 1
},
'W' => {
'@devs' => 1
}
}
);
%groups = (
'@devs' => {
'alice' => 'master',
'bob' => 'master'
},
'@wbr' => {
'firefox' => 'master',
'lynx' => 'master'
}
);
That's a lot smaller, and allows orders of magintude more repos and groups to
be supported.
<a name="_access_rules_for_individual_repos_split_config_"></a>
#### access rules for individual repos (split config)
If, on the other hand, you had the repos listed individually, (as in `repo
lynx firefox`), then the main config file would now look like this:
%repos = ();
%split_conf = (
'firefox' => 1,
'lynx' => 1
);
And each individual repo's configuration would go its own directory. For
instance, `~/repositories/lynx.git/gl-conf` would look like this:
%one_repo = (
'lynx' => {
'R' => {
'alice' => 1,
'bob' => 1
},
'W' => {
'alice' => 1,
'bob' => 1
},
'alice' => [
[
0,
'refs/heads/next',
'RW+'
],
[
4,
'refs/heads/master',
'RW'
]
],
'bob' => [
[
1,
'refs/heads/next',
'RW+'
],
[
5,
'refs/heads/master',
'RW'
]
]
}
);
That does not reduce the overall size of the repo config (because you did not
group the repos), but the main repo config is now even smaller!
<a name="_other_optimisations"></a>
### other optimisations
<a name="_disabling_various_defaults"></a>
#### disabling various defaults
The default RC file contains the following lines (we've already discussed the
first one):
$GL_BIG_CONFIG = 0;
$GL_NO_DAEMON_NO_GITWEB = 0;
$GL_NO_CREATE_REPOS = 0;
$GL_NO_SETUP_AUTHKEYS = 0;
`GL_NO_DAEMON_NO_GITWEB` is a very useful optimisation that you *must* enable
if you *do* have a large number of repositories, and do *not* use gitolite's
support for gitweb or git-daemon access (see "[this][gwd]" for details). This will save a
lot of time when you push the gitolite-admin repo with changes. This variable
also controls whether "git config" lines (such as `config hooks.emailprefix =
"[gitolite]"`) will be processed or not.
You should be a lot more careful with `GL_NO_CREATE_REPOS` and
`GL_NO_SETUP_AUTHKEYS`. These are meant for installations where some backend
system already exists that does all the actual repo creation, (including
setting up the proper hooks -- very important for access control), and all the
authentication setup (ssh auth keys), respectively.
Summary: Please **leave those two variables alone** unless you're initials are
"JK" ;-)
<a name="_optimising_the_authkeys_file"></a>
#### optimising the authkeys file
Sshd does a linear scan of the `~/.ssh/authorized_keys` file when an incoming
connection shows up. This means that keys found near the top get served
faster than keys near the bottom. On my laptop, it takes about 2500 keys
before I notice the delay; on a typical server it could be double that, so
don't worry about all this unless your user-count is in that range.
One way to deal with 5000+ keys is to use customised, database-backed ssh
daemons, but many people are uncomfortable with taking non-standard versions
of such a critical piece of the security infrastructure. In addition, most
distributions do not make it painless to use them.
So what do you do?
The following trick uses the Pareto principle (a.k.a the "80-20 rule")
to get an immediate boost in response for the most frequent or prolific
developers. It can allow you to ignore the problem until the next big
increase in your user counts!
Here's how:
* create subdirectories of keydir/ called 0, 1, (maybe 2, 3, etc., also),
and 9.
* in 0/, put in the pubkeys of the most frequent users
* in 1/, add the next most important set of users, and so on for 2, 3, etc.
* finally, put all the rest in 9/
Make sure "9" contains at least 70-90% of the total number of pubkeys,
otherwise this doesn't really help.
You can easily determine who your top users are by runnning something like
this (note the clever date command that always gets you last months log file!)
cat .gitolite/logs/gitolite-`date +%Y-%m -d -30days`.log |
cut -f2 | sort | uniq -c | sort -n -r
<a name="_what_are_the_downsides_"></a>
### what are the downsides?
There are some downsides. The first one applies in all cases:
* If you use the delegation feature, you can no longer define or extend
@groups in a fragment, for security reasons. It will also not let you use
any group other than the @fragname itself (specifically, groups which
contained a subset of the allowed @fragname, which would work normally, do
not work now).
(If you didn't understand all that, you're probably not using delegation,
so feel free to ignore it!)
The following apply if individual ("split") conf files are written, which in
turn only happens if you used repo names instead of group names on the `repo`
lines:
* the compile (gitolite-admin push) is now slower, because it potentially
has to write a few thousand small files instead of one large one. Since
the compile should be relatively infrequent compared to developer access,
this is ok -- the main config file is parsed much faster now, so every hit
to the server will benefit.
* we can no longer distinguish 'repo not found on disk' from 'you dont have
access'. They both now look like 'you dont have access'.
<a name="_storing_usergroup_information_outside_gitolite_like_in_LDAP_"></a>
### storing usergroup information outside gitolite (like in LDAP)
[Please NOTE: this is all about *user* groups, not *repo* groups]
[WARNING: the earlier method of doing this has been discontinued; please see
the commit message for details]
Gitolite now allows usergroup information to be stored outside its own config
file. We'll see "why" first, then the "how".
<a name="_why"></a>
#### why
Large sites often have LDAP servers that already contain user and group
information, including group membership details. Such sites may prefer that
gitolite just pick up that info instead of having to redundantly put it in
gitolite's config file.
Consider this example config for one repo:
repo foo
RW+ = @lead_devs
RW = @devs
R = @interns
Normally, you would also need to specify:
@lead_devs = dilbert alice
@devs = wally
@interns = ashok
However, if the corporate LDAP server already tags these people correctly, and
if there is some way of getting that information out **at run time**, that
would be cool.
<a name="_how"></a>
#### how
All you need is a script that, given a username, queries your LDAP or similar
server, and returns a space-separated list of all the groups she is a member
of. If an invalid user name is sent in, or the user is valid but is not part
of any groups, it should print nothing.
This script will probably be specific to your site. (See contrib/ldap for some
example scripts that were contributed by the Nokia MeeGo team.)
Then set the `$GL_GET_MEMBERSHIPS_PGM` variable in the rc file to the full
path of this program, set `$GL_BIG_CONFIG` to 1, and that will be that.
[gwd]: http://sitaramc.github.com/gitolite/doc/2-admin.html#gwd
<a name="_implementation_notes"></a>
### implementation notes
To understand how big-config works (at least when you're using grouped repos),
we'll first look at how it works without this setting. Think back to the
example at the top, and assume 'alice' is accessing the 'lynx' repo. The
various rights are governed by the following hash elements:
# for the first level checks
$repos{'lynx'}{'R'}{'alice'} = 1
$repos{'lynx'}{'W'}{'alice'} = 1
# for the second level checks
$repos{'lynx'}{'alice'}{'refs/heads/master'} = 'RW';
$repos{'lynx'}{'alice'}{'refs/heads/next'} = 'RW+';
Those elements are explicitly specified in the compiled hash, as you can see
(you don't need to know perl too much to read a hash; just make some educated
guesses if needed!)
Now look at the compiled hash produced when `GL_BIG_CONFIG` is set. In place
of both 'firefox' and 'lynx' you have '@wbr', and similarly '@devs' for both
'alice' and 'bob'. In addition, there is a group hash at the bottom that
lists each group and its members.
When 'alice' tries to access the 'lynx' repo, gitolite collects all the group
names that these names belong to, so '@devs' is added to the list of 'user'
names that 'alice' inherits permissions from, and '@wbr' is added to the list
of 'repo' names that 'lynx' inherits from. This means that the final access
inherits all permissions pertaining to the following combinations:
alice, lynx
alice, @wbr
@devs, lynx
@devs, @wbr
(Actually there are 3 more... try and guess what they may be!)
Anyway, all ACL rules for these combinations are clubbed together to make the
composite set of rules that 'alice' accessing 'lynx' is subject to.

View file

@ -1,144 +0,0 @@
## delegating access control responsibilities
[Thanks to jeromeag for forcing me to think through this...]
----
In this document:
* <a href="#_lots_of_repos_lots_of_users">lots of repos, lots of users</a>
* <a href="#_splitting_up_the_set_of_repos_into_groups">splitting up the set of repos into groups</a>
* <a href="#_delegating_ownership_of_groups_of_repos">delegating ownership of groups of repos</a>
* <a href="#_other_notes">other notes</a>
* <a href="#_security_philosophy_note">security/philosophy note</a>
----
<a name="_lots_of_repos_lots_of_users"></a>
### lots of repos, lots of users
Gitolite tries to make it easy to manage access to lots of users and repos,
exploiting commonalities wherever possible. It lets you specify bits and
pieces of the access control separately -- i.e., *all* the access specs for a
certain repo need not be together; they can be scattered, which makes it
easier to manage the sort of slice and dice needed in that example.
But eventually the config file will become too big. If you let only one
person have control, he could become a bottleneck. If you give it to multiple
people, they might make mistakes or stomp on each others' work accidentally.
The best way is to divide up the config file and give parts of it to different
people.
Ideally, we would delegate authority for *groups* of repos, not individual
repos, otherwise it doesn't scale. It would also be nice if we could prevent
an admin from creating access rules for *any* repo in the system -- i.e., set
limits on what repos he can control. This would be a nice "security" feature.
Delegation offers a way to do all that. Note that delegated admins cannot
create or remove users, nor can they define new repos. They can only define
access control rules for a set of repos they have been given authority for.
----
It's easier to show how it all works with an example instead of long
descriptions.
<a name="_splitting_up_the_set_of_repos_into_groups"></a>
### splitting up the set of repos into groups
To start with, recall that gitolite allows you to specify **groups** (of users
or repos, same syntax). So the basic idea is that the main config file
(`conf/gitolite.conf` in your admin repo clone) will specify some repo groups:
# group your projects/repos however you want
@webbrowser_repos = firefox lynx
@webserver_repos = apache nginx
@malware_repos = conficker storm
# any other config as usual, including access control lines for any of the
# above projects or groups
<a name="_delegating_ownership_of_groups_of_repos"></a>
### delegating ownership of groups of repos
Once the repos are grouped, give each person charge of one or more groups.
For example, Alice may be in charge of all web browser development projects,
Bob takes care of web servers, and Mallory, as [tradition][abe] dictates, is
in charge of malware ;-)
[abe]: http://en.wikipedia.org/wiki/Alice_and_Bob#List_of_characters
You do this by adding files with specific names to the `gitolite-admin` repo:
# 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+ NAME/ = sitaram
RW NAME/conf/fragments/webbrowser_repos = alice
RW NAME/conf/fragments/webserver_repos = bob
RW NAME/conf/fragments/malware_repos = mallory
This uses gitolite's ability to restrict pushes by file/dir name being changed
-- the syntax you see above ensures that, while "sitaram" does not have any
NAME based restrictions, the other 3 users do. See `conf/example.conf` for
syntax and notes.
As you can see, **for each repo group** you want to delegate authority over,
there's a rule for a **corresponding file** in `conf/fragments` in the
`gitolite-admin` repo. If you have write access to that file, you are allowed
to define rules for repos in that repo group.
In other words, we use gitolite's file/dir NAME-based permissions to "enforce"
the separation between the delegated configs!
Here's how to use this in practice:
* Alice clones the `gitolite-admin` repo, and adds a file called
`conf/fragments/webbrowser_repos.conf`
* she writes in this file any access control rules for the "firefox" and
"lynx" repos. She should not write access rules for any other project --
they will be ignored
* Alice then commits and pushes to the `gitolite-admin` repo
Naturally, a successful push invokes the post-update hook that the admin repo
has, which eventually runs the compile script. The **net effect** is as if
you appended the contents of all the "fragment" files, in alphabetical order,
to the bottom of the main file.
<a name="_other_notes"></a>
### other notes
The "include" statement cannot be used in fragment files, for security
reasons.
----
<a name="_security_philosophy_note"></a>
### security/philosophy note
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 delegated 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! It's pretty easy to setup the
*software* itself system-wide, so that many users can use it without all the
"easy install" fuss. See the "system install / user setup" section in
doc/1-INSTALL.mkd for details.

View file

@ -1,213 +0,0 @@
## developer/patch maintainer notes
In this document:
* <a href="#_current_development_version">current development version</a>
* <a href="#_testing_status_of_gitolite_v2_0rc1">testing status of gitolite v2.0rc1</a>
* <a href="#_migration_to_gitolite_v2_x">migration to gitolite v2.x</a>
* <a href="#_general_stuff">general stuff</a>
* <a href="#_the_rc_file">the rc file</a>
* <a href="#_modules">modules</a>
* <a href="#_that_bindir_thing">that 'bindir' thing</a>
* <a href="#_from_perl">from perl</a>
* <a href="#_from_shell">from shell</a>
* <a href="#_OUTLIER_">OUTLIER!</a>
* <a href="#_special_types_of_setups">special types of setups</a>
* <a href="#_Fedora">Fedora</a>
----
<a name="_current_development_version"></a>
### current development version
The current gitolite development version is v2.0rc1. Unless there is a
serious security problem, *or* one of my large users [i.e., anyone whose name
is in doc/who-uses-it.mkd (grin!)] needs it, all future changes will now
happen here.
The commit looks *huge*, but it's mostly just large chunks of code moving
around; there's not a whole lot of new code. However, I do apologise if
anyone has their local changes conflicted when merging or rebasing against
this version, and I promise to help as much as I can.
<a name="_testing_status_of_gitolite_v2_0rc1"></a>
#### testing status of gitolite v2.0rc1
Pretty much all the major features have been properly tested using the test
suite. The following exceptions exist:
* basic, manual, testing only
* most admin defined commands
* smart http
* mob branches
* not yet tested
* mirroring
* things which I have no easy way to test
* controlling gitweb authentication using gitolite (as described [here][gw])
* SVN passthru
<a name="_migration_to_gitolite_v2_x"></a>
### migration to gitolite v2.x
In general, the procedure for migrating described in the install document
should suffice. Even the rc file hasn't really changed much from the latest
versions in v1.x, except that if you add a new variable to it you must also
add it to the @EXPORT list in `src/gitolite_rc.pm`.
<a name="_general_stuff"></a>
### general stuff
* all scripts and libraries must be in the same directory. However, RPM/DEB
packagers can put the libraries where they want, as long as they can be
found in perl's default `@INC`.
* gl-auth-command **requires** an actual `~/.gitolite.rc` (except if your
initials are "JK" or "DG", in which case `/etc/gitolite/gitolite.rc` also
works!) It knows how to look around and set env vars etc correctly
* all programs except gl-auth-command **require** the environment variables
`GL_RC` and `GL_BINDIR` set properly. Your best bet is to run them *via*
gl-auth-command, like so:
path/to/gl-auth-command -e other_program other_program_arguments
In any case none of these programs are meant to be run manually -- pretty
much all of them are run via gl-auth-command or from something that was
forked from it so the variables *will* exist during normal operation.
<a name="_the_rc_file"></a>
### the rc file
The 'rc' file has one major change from v1: any new values in the rc file need
to be added to the @EXPORT list in `src/gitolite_rc.pm`.
<a name="_modules"></a>
### modules
There are 3 "modules" (`gitolite_rc`, `gitolite_env`, and `gitolite` itself).
Their purposes should be fairly obvious.
<a name="_that_bindir_thing"></a>
### that 'bindir' thing
The importance of `GL_BINDIR` is that the command= argument in
`~/.ssh/authorized_keys` must be a full path, ideally, and the compile script
gets this from `GL_BINDIR`.
<a name="_from_perl"></a>
#### from perl
* for frequently run perl programs, I prefer my method
* gl-auth-command -- this is invoked with a full path
* gl-time -- same as above
* "their" ideal is "FindBin". I will use it only on manually or
infrequently run programs
* gl-setup-authkeys (external shim to compile keys separately from PTA)
<a name="_from_shell"></a>
#### from shell
* a perl program called gl-query-rc finds its own BINDIR (using my perl
method, not FindBin). This is suitable for calling from shell scripts
as `${0%/*}/gl-query-rc GL_BINDIR`
* gl-mirror-shell (frequent use)
* gl-setup
* gl-tool
<a name="_OUTLIER_"></a>
#### OUTLIER!
* gl-dont-panic is an outlier. For some silly reason I have the notion that
even if it runs from /tmp it should get the right values, so it is the
only one that interrogates `~/.ssh/authorized_keys` to get the actual
BINDIR in use!
<a name="_special_types_of_setups"></a>
### special types of setups
<a name="_Fedora"></a>
#### Fedora
Fedora has a very special setup, as follows:
* each user has his own userid and login
* his/her ~/.ssh/authkeys file (containing only his/her key) has a
"command=" clause invoking gl-auth-command
* trusted users have "gl-auth-command -s" meaning they can get a shell if
they want to
* actual git repos are under "git" (or some such), and include the chmod g+s
(git init --shared) unix perms tricks for shared access
* but since they're coming through gl-auth, branch-level acls are in effect
* the gitolite config file is generated from some database and compiled (all
via cron)
* the keydir/ is empty; in fact they probably don't use the admin repo at
all, AFAIK
The most important implication of this setup is that **the RC file is no
longer is `$HOME` of the 'git' user**. They keep it in
`/etc/gitolite/gitolite.rc`. This means that a properly setup rc file must
already be present in `/etc/gitolite/gitolite.rc` before doing any such
installs.
----
# **Why v2?**
I went onto `#perl` to ask some question about setpriority() and got yelled at
for writing "horrible code". And that was one of the kinder comments; my
rather fragile ego is trying to forget the rest ;-)
They also gave me a link to a PDF book, "Modern Perl" by 'chromatic'. Nice
book; one of the first things you learn from it is that you should not go to
`#perl` for general help.
Anyway, the summary of the collective angst of `#perl` (well 2 people anyway)
was: use Getopt::Long, FindBin, 'use lib', a library for HTTP stuff, stop
prefixing subs with '&', and get rid of the huge number of 'our' declarations.
That last item is the only one I totally agree with, because it was on my long
term todo list anyway. And 'use lib' sorta goes with it, so that's fine too.
And as soon as I found that vim colors the sub names differently if you take
out the '&' I decided I'd do that too :-) [But honestly, if `&sub` is so bad
shouldn't "man perlsub" at least say something negative about it, other than
"disables prototype checking", which doesn't matter here since I'm not using
prototypes?]
As for the rest, FindBin brings in a good 1000+ lines for something that I do
in a line or two (since I don't care about all the pathological edge cases).
Getopt::Long is 2649 lines to replace the code below [note that there *is*
only one possible option to this command, and it is *never* run manually
either, so I don't need any fancy features]:
my $shell_allowed = 0;
if (@ARGV and $ARGV[0] eq '-s') {
$shell_allowed = 1;
shift;
}
Apparently TMTOWTDI has given way to TOOWTDI.
Anyway, I spent a few hours refactoring it. And I do thank them for pushing
me to stop being lazy on the "our" business.
[gw]: https://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#_easier_to_link_gitweb_authorisation_with_gitolite

View file

@ -1,159 +0,0 @@
# how gitolite uses ssh
Although other forms of authentications exist (see
[doc/gitolite-without-ssh.mkd][ws]), ssh is the one that most git users use.
***Therefore, gitolite is (usually) heavily dependent on ssh***.
[ws]: http://sitaramc.github.com/gitolite/doc/gitolite-without-ssh.html
Most people didn't realise this, and even if they did they didn'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 :-)
In this document:
* <a href="#_ssh_basics">ssh basics</a>
* <a href="#_how_does_gitolite_use_all_this_ssh_magic_">how does gitolite use all this ssh magic?</a>
* <a href="#_restricting_shell_access_distinguishing_one_user_from_another">restricting shell access/distinguishing one user from another</a>
* <a href="#_restricting_branch_level_actions">restricting branch level actions</a>
----
<a name="_ssh_basics"></a>
### 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.
**This is the backbone of what makes gitolite work; please make sure you
understand this**
<a name="_how_does_gitolite_use_all_this_ssh_magic_"></a>
### 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
<a name="_restricting_shell_access_distinguishing_one_user_from_another"></a>
#### 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]/gl-auth-command sitaram",[more options] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA18S2t...
command="[path]/gl-auth-command 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]/gl-auth-command 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 `gl-auth-command` 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 is has 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.
<a name="_restricting_branch_level_actions"></a>
#### 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 y 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.

View file

@ -1,75 +0,0 @@
# gitolite without ssh
Gitolite does not actually need ssh. If you managed to read
[doc/gitolite-and-ssh.mkd][gas], you may have realised that the crux of
gitolite is that it be eventually invoked as
/full/path/to/gl-auth-command 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).
[gas]: http://sitaramc.github.com/gitolite/doc/gitolite-and-ssh.html
### sidebar: authentication versus authorisation
> Just for completeness, I'd like to loosely define these two animals.
> **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.
### what does gitolite actually do
With that background, here's the truth about gitolite:
> ***Gitolite does not do authentication. It only does authorisation.***
Yes, the default mode of using gitolite does use ssh keys, but all it's doing
is helping you **setup** ssh-based authentication **as a convenience to you**.
But nothing forces you to use it. The best example: there are no ssh keys
involved if you setup [smart http][sh] mode.
[sh]: http://sitaramc.github.com/gitolite/doc/http-backend.html
In other words, authentication is the job of `sshd` or `httpd`, not gitolite.
### using other authentication systems with gitolite
The bottom line in terms of how to invoke gitolite has been described up at
the top, 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.
[Side note: it also expects that the environment variable
`SSH_ORIGINAL_COMMAND` contain the command that the client sent (typically a
git-receive-pack or git-upload-pack command), or in some CGI variables when
using [smart http][sh] mode.]
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).
One common example is LDAP. 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.
Finally, gitolite allows you to store *group* information externally too. See
[here][ldap] for more on this.
[ldap]: http://sitaramc.github.com/gitolite/doc/big-config.html#_storing_usergroup_information_outside_gitolite_like_in_LDAP_

View file

@ -1,489 +0,0 @@
# the access control file `gitolite.conf`
In this document:
* <a href="#_syntax">syntax</a>
* <a href="#_continuation_lines">continuation lines</a>
* <a href="#_include_files">include files</a>
* <a href="#_basic_access_control">basic access control</a>
* <a href="#_how_rules_are_matched">how rules are matched</a>
* <a href="#_branches_tags_and_specifying_refex_es">branches, tags, and specifying "refex"es</a>
* <a href="#_groups">groups</a>
* <a href="#_the_special_all_group">the special `@all` group</a>
* <a href="#_advanced_access_control">advanced access control</a>
* <a href="#_creating_and_deleting_branches">creating and deleting branches</a>
* <a href="#_deny_rules">"deny" rules</a>
* <a href="#_IMPORTANT_NOTES_ABOUT_DENY_RULES_">***IMPORTANT NOTES ABOUT "DENY" RULES***:</a>
* <a href="#_summary_permissions">summary: permissions</a>
* <a href="#_virtual_ref_types">virtual "ref"-types</a>
* <a href="#_other_tips">other tips</a>
* <a href="#_splitting_up_rules_into_rulesets">splitting up rules into rulesets</a>
* <a href="#_gitweb_and_daemon">gitweb and daemon</a>
* <a href="#_repo_specific_git_config_commands">repo specific `git config` commands</a>
* <a href="#_repo_owner_description_line_for_gitweb">repo owner/description line for gitweb</a>
Gitolite has an advanced access control language that is designed to be
powerful but easy to use. Other objectives were that it should be even easier
to read, review and audit the rules, and it should scale to thousands of repos
and users. There was also, in the author's mind, a desperate need to create
something as different as possible from the brain-dead, nausea-inducing
"Windows INI" style syntax that some other popular tools seem to favour.
This document describes the syntax and semantics of the access control rules
and other configuration directives in the `gitolite.conf` file.
<a name="_syntax"></a>
### 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)
<a name="_continuation_lines"></a>
#### continuation lines
There are no continuation lines -- gitolite does not process C-style
backslash-escaped newlines as anything special. However, the section on
"groups" will tell you how you can break up large lists of names in a group
definition into multiple lines.
<a name="_include_files"></a>
#### 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" from the same directory as
the main config file. You can also use an absolute path if you like, although
in the interests of cloning the admin-repo sanely you should avoid doing this!
You can also use a glob, as in:
include "*.conf"
which will include all the ".conf" files from the directory in which the main
config file exists.
Files that have been already processed once are skipped, with a warning.
[Advanced users: the include statement cannot be used inside a delegated
config file, for security reasons].
<a name="_basic_access_control"></a>
### basic access control
Here's a very basic set of rules:
repo gitolite-admin
RW+ = sitaram
repo testing
RW+ = @all
repo gitolite simplicity
RW+ = sitaram dilbert
RW = alice ashok
R = wally
It should be easy to guess what most of this means:
* `R` means "read" permission
* `RW` means "read and write", but no rewind
* `RW+` means "read and write", with rewind allowed
A "rewind" is more often called a "non-fast forward push"; see git docs for
what that is. The `+` was chosen because it is part of the "git push" syntax
for non-ff pushes.
In a later section you'll see some more advanced permissions.
<a name="_how_rules_are_matched"></a>
#### how rules are matched
It's important to understand that there're two levels at which access control
happens. Please see [this][l2] for details, especially about the first level
check. Much of the complexity applies only to the second level check, so that
is all we will be discussing here. This check is done by the update hook, and
determines whether the push succeeds or fails.
[l2]: http://sitaramc.github.com/gitolite/doc/3-faq-tips-etc.html#_two_levels_of_access_rights_checking
For basic permissions like this, matching is simple. Gitolite already knows:
* the user
* the repo
* the branch or tag ("ref") being pushed
* whether it is a normal (ff) push or a rewind (non-ff) push.
Gitolite goes down the list of rules matching the user, repo, and the ref.
The first matching rule that has the permission you're looking for (`W` or
`+`), results in success. A fallthrough results in failure.
<a name="_branches_tags_and_specifying_refex_es"></a>
#### branches, tags, and specifying "refex"es
One of the original goals of gitolite was to allow access control at the
branch/tag (aka "ref") level. The git source code contains a sample update
hook that has the following in it:
# from Documentation/howto/update-hook-example.txt:
refs/heads/master junio
+refs/heads/pu junio
refs/heads/cogito$ pasky
refs/heads/bw/.* linus
refs/heads/tmp/.* .*
refs/tags/v[0-9].* junio
If you did this in gitolite, this is what the equivalents would be:
repo git
RW master$ = junio # line 1
RW+ pu$ = junio # line 2
RW cogito$ = pasky # line 3
RW bw/ = linus # line 4
RW tmp/ = @all # line 5
RW refs/tags/v[0-9] = junio # line 6
The following points will help you understand these rules. (Git recap:
branches and tags together are called "ref"s in git. A branch ref usually
looks like "refs/heads/foo", while a tag ref looks like "refs/tags/bar")
* the general syntax of a paragraph of rules is:
# start line:
repo [one or more repos and/or repo groups]
# followed by one or more permissions lines:
[permission] [zero or more refexes] = [one or more users]
* a **refex** is a *perl regex* that matches a ref. When you try to push a
commit to a branch or a tag, that "ref" is matched against the refex part
of each rule
* if the refex does not start with `refs/`, gitolite assumes a prefix of
`refs/heads/`. This is useful because *branch* matching is the most
common case, as you can see this applies to lines 1 through 5 here.
* if no refex appears, the rule applies to all refs in that repo
* refexes are prefix-matched (they are internally anchored with `^` before
being used). This means only the beginning of the actual ref needs to
match the refex, unless the refex has an explicit `$` meta-character at
the end (like the first 3 lines in our example do).
Line 5, for instance, allows anyone to push a branch inside the "tmp/"
namespace, while line 6 provides the ability to push version tags; "v1",
"v1.0", "v2.0rc1", all match the criterion specified by `v[0-9]` because
this is a prefix match only.
<a name="_groups"></a>
#### groups
Gitolite allows you to define **groups** of repos. users, or even refexes. A
group is semantically (but *not* syntactically) like a `#define` in C. Here
is an example of each kind:
@oss_repos = gitolite linux git perl rakudo entrans vkc
@staff = sitaram some_dev another-dev
@important = master$ QA_done refs/tags/v[0-9]
The syntax of a group definition is simply:
@groupname = [one or more names]
A group can *accumulate* values. For example:
@staff = sitaram some_dev another-dev
@staff = au.thor
is the same as
@staff = sitaram some_dev another-dev au.thor
This is more convenient than continuation lines, because it allows you to add
to a group anywhere. Many people generate their gitolite.conf itself from
some *other* database, and it is very useful to be able to do this sort of
thing.
Groups can include other groups, and the included group will be expanded to
whatever value it *currently* has:
@staff = sitaram some_dev another-dev # line 1
@staff = au.thor # line 2
@interns = indy james # line 3
@alldevs = bob @interns @staff # line 4
"@alldevs" expands to 7 names now. However, remember that the config file is
parsed in a single-pass, so later *additions* to a group name cannot affect
earlier *uses* of it. If you moved line 2 to the end, "@alldevs" would only
have 6 names in it.
<a name="_the_special_all_group"></a>
##### the special `@all` group
There's a special group called `@all` that includes all authenticated users
when used as a username; you've seen examples of it earlier.
[Advanced users: also see the entry for `GL_ALL_INCLUDES_SPECIAL` in
[doc/gitolite.rc.mkd][rcdoc].]
When used as a reponame, it includes all repos.
<a name="_advanced_access_control"></a>
### advanced access control
The previous section is sufficient for most common needs, but gitolite can go
a lot further than that.
<a name="_creating_and_deleting_branches"></a>
#### creating and deleting branches
Since the beginning of gitolite, `RW` gave the ability, not only to update,
but to *create* a branch (that matched the refex). Similarly, `RW+` meant
being able to not only rewind, but also delete a ref. Conceptually, a rewind
is almost the same as a delete+push (the only difference I can see is if you
had core.logAllRefUpdates set, which is *not* a default setting).
However, there seem to be cases where it is useful to distinguish these cases.
Arguments can be made on all sides if you're dealing with new users, so
gitolite supports that.
We'll look at the delete/rewind case in detail first:
* if the rules for a repo do not contain a `D` anywhere, then `RW+` will
allow both rewind and delete operations. Apart from being more convenient
if you don't need this separation, this also ensures backward
compatibility for setups created before this separation feature was added
to gitolite).
* if, however, *any* of the rules for a repo contains a `D` (example: `RWD`,
`RW+D`, etc) then `RW+` by itself will permit only a rewind, not a delete
The same thing applies to create/push, where if you have a permissions like
`RWC` or `RW+C` anywhere, a simple `RW` or `RW+` can no longer *create* a new
ref.
You can combine the `C` and `D` also. Thus, the set of permissions you now
know about are, in regex syntax: `R|RW+?C?D?`. See a later section for the
full set of permissions possible.
Some usage hints:
* if you find that `RW+` no longer allows creation/deletion but you can't
see a `C`/`D` permission in the rules, remember that gitolite allows a
repo config to be specified in multiple places for convenience, included
delegated or included files. Be sure to search everywhere :)
* a quick way to make this the default for *all* your repos is:
repo @all
RWCD dummy-branch = foo
where foo can be either the administrator, or if you can ignore the
warning message when you push, a non-existant user.
<a name="_deny_rules"></a>
#### "deny" rules
Take a look at the following snippet, which *seems* to say that "bruce" can
write versioned tags (anything containing `refs/tags/v[0-9]`), but the other
staffers can't:
@staff = bruce whitfield martin
[... and later ...]
RW refs/tags/v[0-9] = bruce
RW refs/tags = @staff
But that's not how the matching works. As long as any refex matches the
refname being updated, it's a "yes". Since the second refex (which says
"anything containing `refs/tags`") is a superset of the first one, it lets
anyone on `@staff` create versioned tags, not just Bruce.
So how do we say "these people can create any tags except tags that look like
this pattern"?
One way to fix this is to allow "deny" rules. We make a small addition to the
permissions syntax, and define a more rigorous, ordered, interpretation.
Let's recap the **existing semantics**:
> The first matching refex that has the permission you're looking for (`W`
> or `+`), results in success. A fallthrough results in failure.
Here are the **new semantics**, with changes from the "main" one in bold:
> The first matching refex that has the permission you're looking for (`W`
> or `+`) **or a minus (`-`)**, results in success **or failure,
> respectively**. A fallthrough **also** results in failure.
So the example we started with becomes, if you use "deny" rules:
RW refs/tags/v[0-9] = bruce
- refs/tags/v[0-9] = @staff
RW refs/tags = @staff
And here's how it works:
* for non-version tags, only the 3rd rule matches, so anyone on staff can
push them
* for version tags by bruce, the first rule matches so he can push them
* for version tags by staffers *other than bruce*, the second rule matches
before the third one, and it has a `-` as the permission, so the push
fails
<a name="_IMPORTANT_NOTES_ABOUT_DENY_RULES_"></a>
##### ***IMPORTANT NOTES ABOUT "DENY" RULES***:
* deny rules do NOT affect read access. They only apply to write access.
* when using deny rules, the order of your rules starts to matter, where
earlier it did not. If you're just starting to add a deny rule to an
existing ruleset, it's a good idea to review the entire ruleset once, to
make sure you're doing it right.
<a name="_summary_permissions"></a>
### summary: permissions
The full set of permissions, in regex syntax: `-|R|RW+?C?D?`. This expands to
one of `-`, `R`, `RW`, `RW+`, `RWC`, `RW+C`, `RWD`, `RW+D`, `RWCD`, or
`RW+CD`. and by now you know what they all mean.
[Side note: There is one more very important permissions to be dealt with --
the standalone `C`, which is not really a "ref" level permission and can be
found in doc/wildcard-repositories.mkd.]
<a name="_virtual_ref_types"></a>
### virtual "ref"-types
This is a highly advanced topic; see [doc/virtualrefs-and-scoring.mkd][vs] for
details.
<a name="_other_tips"></a>
### other tips
<a name="_splitting_up_rules_into_rulesets"></a>
#### splitting up rules into rulesets
Gitolite lets you specify access rules for a repo in bits and pieces. This
can be very convenient sometimes. Let's say you have a mix of open source and
closed source projects, and "bosses" should have read access to all projects,
and everyone should have read access to open source projects. Assuming the
appropriate group definitions, this would work:
# all bosses have read access to all projects
repo @open @closed @topsecret
R = @bosses
# everyone has read access to "open" projects
repo @open
R = @bosses @devs @interns
If you notice that `@bosses` are given read access to `@open` via both rules,
don't worry that this causes some duplication or inefficiency. It doesn't :-)
Elsewhere in the file, you would specify access for individual repos (like RW,
RW+, etc). Gitolite combines all of these access rules, maintaining the
textual order in which they occur, when authorising a push.
And although this example used groups, you can use reponames as well, or mix
and match them. You can even distribute rulesets across multiple "include"
files if you wish.
Just remember that if you use [deny rules][dr] anywhere then the *order of the
rules matters*!
[dr]: http://sitaramc.github.com/gitolite/doc/gitolite.conf.html#_deny_rules
This feature also helps people who generate their gitolite.conf itself from
some *other* database -- it allows them much more flexibility in how they
generate rules.
<a name="_gitweb_and_daemon"></a>
#### gitweb and daemon
Gitolite allows you to specify access for git-daemon and gitweb. See
[this][gwd] for more on this.
[gwd]: http://sitaramc.github.com/gitolite/doc/2-admin.html#gwd
<a name="_repo_specific_git_config_commands"></a>
#### repo specific `git config` commands
(Thanks to teemu dot matilainen at iki dot fi)
Sometimes you want to specify `git config` settings for some of your repos.
For example, you may have a custom post-receive hook that sends an email when
a push happens, and this hook needs to know whom to send the email to, etc.
You can set git config values by specifying something like this within a
"repo" paragraph:
example usage: if you placed a hook in hooks/common that requires
configuration information that is specific to each repo, you could do this:
repo gitolite
config hooks.mailinglist = gitolite-commits@example.tld
config hooks.emailprefix = "[gitolite] "
config foo.bar = ""
config foo.baz =
The syntax is simple:
config sectionname.keyname = [optional value_string]
This does either a plain "git config section.key value" (for the first 3
examples above) or "git config --unset-all section.key" (for the last
example). Other forms (--add, the `value_regex`, etc) are not supported.
**Note**: this won't work unless the rc file has the right settings; please
see comments around the variable `$GL_GITCONFIG_KEYS` in doc/gitolite.rc.mkd
for details and security information.
[rcdoc]: http://sitaramc.github.com/gitolite/doc/gitolite.rc.html
<a name="_repo_owner_description_line_for_gitweb"></a>
#### repo owner/description line for gitweb
Including a line like this:
gitolite "Sitaram Chamarty" = "fast, secure, access control for git in a corporate environment"
sets the owner name and description for gitweb. The general syntax is very
simple, just use one of:
reponame = "some description string in double quotes"
reponame "owner name" = "some description string in double quotes"
Note: setting a description also gives gitweb access; you do not have to give
gitweb access explicitly (as described or linked above) if you're specifying a
description.

View file

@ -1,373 +0,0 @@
# configuring gitolite's advanced features -- the `.gitolite.rc` file
This is the documentation for the contents of the "rc" file
(`$HOME/.gitolite.rc`) on the server. Until now this documentation was
inline, within the rc file itself, but it has grown too large, too unwieldy,
and too difficult to grok for people new to gitolite.
The documentation follows approximately the same order as the sample variables
in the (now reorganised) example "rc" file.
In this document:
* <a href="#_variables_that_should_not_be_touched_at_all">variables that should not be touched at all</a>
* <a href="#_most_often_used_changed_variables">most often used/changed variables</a>
* <a href="#_variables_with_an_efficiency_performance_impact">variables with an efficiency/performance impact</a>
* <a href="#_variables_with_a_security_impact">variables with a security impact</a>
* <a href="#_less_used_changed_variables">less used/changed variables</a>
* <a href="#_rarely_changed_variables">rarely changed variables</a>
* <a href="#_constants_that_aren_t_">constants that aren't!</a>
[Note: in perl, there is no actual boolean. The undefined value, the number
'0', and the empty string, are all 'false'. Everything else is 'true'. It is
thus common to use just 0/1 for false/true].
<a name="_variables_that_should_not_be_touched_at_all"></a>
### variables that should not be touched at all
The first section does not need too much elaboration. Let's just say bad
things happen if you change them.
<a name="_most_often_used_changed_variables"></a>
### most often used/changed variables
* `$GL_WILDREPOS`, boolean, default 0
Setting this variable lets your users create repositories based on wild
cards, hand out R and RW permissions to other users to collaborate, etc.
See [doc/wildcard-repositories.mkd][wild] for lots of info on this.
* `$PROJECTS_LIST`, filename, default `~/projects.list`
This is for gitweb users only. Gitweb setup has a variable called
`$projects_list` (please see gitweb docs for more on this). Set this to
the same value as that one.
* `$REPO_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
change this variable to `0027` (which gets you `rwxr-x---`), then add the
apache or httpd user running the webserver as a member of the 'gitolite'
group.
Please note the syntax; the leading 0 is required. If you change it
*after* the install is complete, you'll have to do some chmod's also to
adjust permissions of files and directories that have already been
created.
<a name="_variables_with_an_efficiency_performance_impact"></a>
### variables with an efficiency/performance impact
* `$GL_BIG_CONFIG`, boolean, default 0
This is the most common setting for efficiency in handling large repo/user
groups. This is a very powerful setting; please read
[doc/big-config.mkd][bc] for all the details you might need.
There are 3 other settings related to big configs. They are changed only
in rare cases, however, so are described later.
* `$GL_NO_DAEMON_NO_GITWEB`, boolean, default 0
If you have *lots* of repos, and you're *not* using gitweb or daemon, you
should probably set this on for efficiency. Despite the name, it also
blocks repo config settings. Please read [doc/big-config.mkd][bc] for
more details.
* `$GL_NICE_VALUE`, boolean, default undef
The nice value to run under. Applicable only if it is greater than 0.
Please do NOT set it if your bits/resource.h does not define PRIO_PROCESS
is 0. For Linux this is true...
* `$BIG_INFO_CAP`, number, default 20
See [using patterns to limit output][limit] for details.
<a name="_variables_with_a_security_impact"></a>
### variables with a security impact
**IMPORTANT NOTE**
This section describes variables that, if not carefully used, can cause
security issues. It also includes variables which I personally do not use and
do not have the ability to test thoroughly
Using non-default value for these variables voids the security reward in the
README. This does *not* mean they are less important or that I will ignore
problems; it just means *my* ability to catch problems may be limited by my
test suite, my actual production use, my time, and sometimes (LDAP comes to
mind) even my skill or resources available to me, and that therefore I depend
on feedback from my users to find or fix issues.
* `$GL_ALL_READ_ALL`, boolean, default undef
Eliminates the access control check for read access. Makes things much
(**much**!) faster when you have 10,000 projects and the compiled conf
file is more than 20MB in size! **Double check with your boss or have a
new job lined up before setting this on!**
* `$GIT_PATH`, string, default empty
If git on your server is on a standard path (that is `ssh git@server git
--version` works), leave this setting as is. Otherwise, find out where it
is and use that value here, for example `GIT_PATH="/opt/bin/";`
* `$GL_GITCONFIG_KEYS`, string, default empty
This setting allows the repo admin to define acceptable gitconfig keys.
Gitolite allows you to set git repo options using the "config" keyword;
see doc/gitolite.conf.mkd 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 `$GL_GITCONFIG_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 perl regular
expression patterns, and any one of them must match). For example:
`$GL_GITCONFIG_KEYS = "core\\.logAllRefUpdates core\\..*compression";`
allows repo admins to set one of those 3 config keys (yes, that second
pattern matches two settings from "man git-config", if you look).
The third choice (which you may have guessed already if you're familiar
with regular expressions) is to allow anything and everything:
`$GL_GITCONFIG_KEYS = ".*";`
NOTE that due to some quoting and interpolation issues I have not been
able to look at, a literal "." needs to be specified in this string as
`\\.` (two backslashes and a dot). So this is how you'd allow any keys in
the "foo" category: `$GL_GITCONFIG_KEYS = "foo\\..*";`
* `$GL_GITCONFIG_WILD`, boolean, default 0
This setting allows gitconfig keys even for wild repos. This is an
efficiency issue more than a security issue, since this requires trawling
through all of `$REPO_BASE` looking for stuff :)
* `$GL_NO_CREATE_REPOS`, boolean, default 0
DO NOT CHANGE THIS unless you have other means to create repos and
correctly populate them with the required hooks. No hooks, no access
control; you have been warned!
* `$GL_NO_SETUP_AUTHKEYS`, boolean, default 0
DO NOT CHANGE THIS unless you have other means to setup the authkeys file
(`~/.ssh/authorized_keys`). In an extreme case, if you switch this on
without also fixing up the authkeys file, users who you think you deleted
may still have access. All in all, please be careful, as with any change
that affects ssh.
* `$GL_WILDREPOS_DEFPERMS`, string, default undef
This sets default wildcard permissions for newly created wildcard repos.
If set, this value will be used as the default user-level permission rule
of new wildcard repositories. The user can change this value with the
setperms command as desired after repository creation; it is only a
default.
Example: `$GL_WILDREPOS_DEFPERMS = 'R @all';`
* `$HTPASSWD_FILE`, string, default empty
Gitolite can help users run the htpasswd command in a secure manner (since
gitolite has already identified them by an ssh key). If you want to
enable this, give the variable the absolute path to whatever file apache
(etc) expect to find the passwords in.
Look in [doc/3-faq-tips-etc.mkd][faq] ("easier to link gitweb
authorisation with gitolite" section) for more details on using this
feature.
* `$RSYNC_BASE`, string, default empty
Gitolite can be used to allow fine grained control of the rsync command.
This setting enables the rsync external command helper, by specifying the
base path of all the files that are accessible via rsync. It must be an
absolute path, like `$RSYNC_BASE = "/home/git/up-down";`. Leave it
undefined or set to the empty string to disable the rsync helper.
When enabled, it runs rsync with specific arguments, all presumably filled
in correctly by the client-side rsync. However, I am not an expert on how
rsync may be abused, so if it breaks, you get to keep both pieces!
* `$SVNSERVE`, string, default empty
Gitolite can also be used to gate access (though not at a fine grained
level) to SVN if needed, passing authentication information on to
`svnserve`. This setting allows launching svnserve when requested by the
ssh client. This allows using the same SSH setup for both SVN and git
access. Leave it undefined or set to the empty string to disable svnserve
access.
The setting will look something like (where the %u is substituted with the
username):
$SVNSERVE = "/usr/bin/svnserve -r /var/svn/ -t --tunnel-user=%u";
* hook chaining
* `$UPDATE_CHAINS_TO`, string, default "hooks/update.secondary"
* `$ADMIN_POST_UPDATE_CHAINS_TO`, string, default
"hooks/post-update.secondary"
By default, the update hook in every repo chains to "update.secondary".
Similarly, the post-update hook in the admin repo chains to
"post-update.secondary". If you're fine with the defaults, there's no
need to do anything here. However, if you want to use different names or
paths, change these variables.
* `$GL_ADC_PATH`, string, default undef
This setting enables admin defined commands.
**WARNING**: Use this feature only if (a) you really know what you're
doing and (b) you really, **really**, know what you're doing! Please read
[doc/admin-defined-commands.mkd][adc] for details. This is an extremely
powerful and flexible feature, and naturally anything that flexible can be
a security risk!
* `$GL_GET_MEMBERSHIPS_PGM`, string, default undef
Some sites would like to store group membership outside gitolite, because
they already have it in (usually) their LDAP server, and it doesn't make
sense to be forced to duplicate this information.
Set the following variable to the name of a script that, given a username
as argument, will return a list of groups that she is a member of. See
[doc/big-config.mkd][bc] for more details.
Example: `$GL_GET_MEMBERSHIPS_PGM = "/usr/local/bin/expand-ldap-user-to-groups"`
* `$GL_HTTP_ANON_USER`, string, default undef
Analogous to running mob branches over ssh (as described in
[doc/mob-branches.mkd][mob], this variable -- combined with appropriate
setup described in [doc/http-backend.mkd][smart] -- lets you pretend to
gitolite that unauthenticated HTTP users are actually authenticated as
this user.
<a name="_less_used_changed_variables"></a>
### less used/changed variables
* `$GL_ALL_INCLUDES_SPECIAL`, boolean, default undef
Giving access to @all users (as in `R = @all`) in the config normally
does *not* include the special users "gitweb" and "daemon". If you want
@all to include these two users, set this variable.
* mirroring setup
These two variables enable mirroring support; see
[doc/mirroring.mkd][mirr] for details. The two variables are
`$GL_SLAVE_MODE`, (boolean, default undef), and `$ENV{GL_SLAVES}`,
(environment variable, string, default undef)
Note on the second variable above: you must use single quotes to give it
its value, not double quotes, (like `$ENV{GL_SLAVES} = 'gitolite@server2
gitolite@server3';`). Also note that this is an environment variable, not
a regular perl variable, so mind the syntax if you're not a perl guy :-)
* `$GL_WILDREPOS_PERM_CATS`, string, default "READERS WRITERS"
Originally, we only allowed "R" and "RW" in the setperms command. Now we
allow the admin to define other categories as she wishes (example:
MANAGERS, TESTERS, etc).
This variable is a space-separated list of the allowed categories.
PLEASE, *PLEASE*, read the section in
[doc/wildcard-repositories.mkd][wild] for caveats and warnings. This is a
VERY powerful feature and if you're not careful you could mess up the ACLs
nicely.
This is the internal default if you don't set it (like if you didn't
update your ~/.gitolite.rc with new variables when you upgraded gitolite):
$GL_WILDREPOS_PERM_CATS = "READERS WRITERS";
You can use your own categories in addition to the standard ones; I
suggest you include READERS and WRITERS for backward compatbility though:
$GL_WILDREPOS_PERM_CATS = "READERS WRITERS MANAGERS TESTERS";
<a name="_rarely_changed_variables"></a>
### rarely changed variables
* `$GL_LOGT`, string, default `$GL_ADMINDIR/logs/gitolite-%y-%m.log`
This is the template for location of the log files and format of their
names.
The default produces files like `~/.gitolite/logs/gitolite-2009-09.log`.
If you make up your own templates, **PLEASE MAKE SURE** the directory
exists and is writable; gitolite won't do that for you unless it is the
default, ("$GL_ADMINDIR/logs")
* `$GL_PERFLOGT`, string, default undef
This gives the location of the performance log files. Uncomment and set
this variable if you want performance logging. Performance log files are
kept separate from access log files because they store different, usually
much shorter term, information.
* `$GL_SITE_INFO`, string, default undef
Some installations would like to give their users customised information
(like a link to their own websites, for example) so that users have a
quick way to find some links or information.
If this variable is defined, the "info" command will print it at the end
of the listing.
* `$REPO_BASE`, string, default "repositories"
This is where all the repos go. If it's not an absolute path, it is
considered to be relative to $HOME. Moving all the repositories after the
install has completed is doable: just [disable writes][dwr] to gitolite,
move `~/repositories/*`, change this variable, then re-enable writes.
<a name="_constants_that_aren_t_"></a>
### constants that aren't!
The source file `src/gitolite_rc.pm` defines a few "constants", for example:
$R_COMMANDS=qr/^(git[ -]upload-pack|git[ -]upload-archive)$/;
Let's say you want to disallow the archive feature, you would need to change
this constant.
As of this version, you can now change these "constants" also, simply by
defining a new value for any or all of them in your `~/.gitolite.rc` file.
If you use this to relax some of the patterns involved (for example, the value
of `ADC_CMD_ARGS_PATT`), please be sure you know what you're doing.
[wild]: http://sitaramc.github.com/gitolite/doc/wildcard-repositories.html
[bc]: http://sitaramc.github.com/gitolite/doc/big-config.html
[faq]: http://sitaramc.github.com/gitolite/doc/3-faq-tips-etc.html
[adc]: http://sitaramc.github.com/gitolite/doc/admin-defined-commands.html
[mirr]: http://sitaramc.github.com/gitolite/doc/mirroring.html
[mob]: http://sitaramc.github.com/gitolite/doc/mob-branches.html
[smart]: http://sitaramc.github.com/gitolite/doc/http-backend.html
[dwr]: http://sitaramc.github.com/gitolite/doc/3-faq-tips-etc.html#_disabling_write_access_to_take_backups
[limit]: http://sitaramc.github.com/gitolite/doc/report-output.html#_using_patterns_to_limit_output

View file

@ -1,175 +0,0 @@
## hook propagation in gitolite
Advanced users need to know how hooks propagate, and when. They also need to
know where to place their hooks, and since there appear to be two places to
put them, what takes precedence. I'll try and set out the logic here.
In this document:
* <a href="#_hooks_used_by_gitolite">hooks used by gitolite</a>
* <a href="#_where_do_I_the_admin_put_the_hooks_">**where** do I (the admin) put the hooks?</a>
* <a href="#_the_from_client_method">the "from-client" method</a>
* <a href="#_the_other_3_methods">the other 3 methods</a>
* <a href="#_the_GL_PACKAGE_HOOKS_directory">the `GL_PACKAGE_HOOKS` directory</a>
* <a href="#_the_HOME_gitolite_directory">the `$HOME/.gitolite` directory</a>
* <a href="#_why_two_places_">why two places?</a>
* <a href="#_special_case_the_non_root_method">special case: the "non-root" method</a>
* <a href="#_when_do_hooks_propagate_">**when** do hooks propagate?</a>
<a name="_hooks_used_by_gitolite"></a>
### hooks used by gitolite
Gitolite uses only 2 hooks. **All** repos have an `update` hook, without
which there is no write-level access control (per-branch permissions). The
special **gitolite-admin** repo has a special `post-update` hook, which is
required to do its, umm, special things, like running the "compile" script,
etc.
In addition there is a "sentinel file" -- an empty file called
"gitolite-hooked". We'll see later what this does.
The final objective of all this is that each repo's `hooks/` directory should
get all the hooks that it is meant to get.
<a name="_where_do_I_the_admin_put_the_hooks_"></a>
### **where** do I (the admin) put the hooks?
In general, **all** hooks go into the `hooks/common` directory. Only the
special `post-update` hook meant for the admin repo goes into
`hooks/gitolite-admin`.
Now we'll discuss the locations of these `hooks/common` and
`hooks/gitolite-admin` directories. This depends on which install method you
used.
(Please refer to [doc/1-INSTALL.mkd][0inst] for what these "methods" are).
<a name="_the_from_client_method"></a>
#### the "from-client" method
Let's get this out of the way first, because it is simple: if you're using the
"from-client" method, there's only one place: the `hooks` directory in your
gitolite clone on the client side. This is where you run
`src/gl-easy-install` from. Nothing else in this section is relevant to this
method; skip to the next section ("when do hooks propagate") if you installed
using the "from-client" method.
<a name="_the_other_3_methods"></a>
#### the other 3 methods
<a name="_the_GL_PACKAGE_HOOKS_directory"></a>
##### the `GL_PACKAGE_HOOKS` directory
You might recall that the "root", and "non-root" methods run a command called
`gl-system-install`, the third argument of which is some directory of your
choice (like maybe `/usr/share/gitolite/hooks`). Even though it is not
necessary to know this, internally this becomes the value of the
`$GL_PACKAGE_HOOKS` variable, so in this document we will refer to that
variable instead of the location (because you might choose any location you
like for it).
The "package" method also has the same property, except that the packager has
already decided what that location is, and the package creation/install
process does the equivalent of `gl-system-install`.
So now we know there's a location called `$GL_PACKAGE_HOOKS` where you can
place your hooks.
<a name="_the_HOME_gitolite_directory"></a>
##### the `$HOME/.gitolite` directory
You might also recall that, in these three methods, each **hosting user** has
to run `gl-setup`. This sets up, among other things, `$HOME/.gitolite`
directory, which also contains a `hooks/` directory.
So now there are two places you can put your hooks, apparently.
<a name="_why_two_places_"></a>
#### why two places?
Just think of the "package" and "root" methods for now, even if you're using
the "non-root" method.
In these two methods, it is reasonable to assume that the entire site (or
server) has certain policies that they want to implement using hooks. They
want to enforce these hooks on *each hosting user*. These hooks go into
`$GL_PACKAGE_HOOKS`.
Each hosting user then has the discretion to add his own hooks (modulo name
clashes, which may necessitate hook chaining, etc., like we already do for the
hooks that gitolite cares about). He adds these hooks to his
`$HOME/.gitolite/hooks` directory.
When hooks propagate, the ones in `$GL_PACKAGE_HOOKS` override/overwrite the
ones in `$HOME/.gitolite/hooks`. Otherwise it wouldn't make sense; you
wouldn't be able to enforce site-wide hooks.
[NOTE: due to a minor quirk, the site-wide hooks in `$GL_PACKAGE_HOOKS` also
get copied to `$HOME/.gitolite/hooks` when you "install". I need to fix and
thoroughly test this later; for now, just ignore the extra files you see in
there; they're harmless/redundant (TODO)]
<a name="_special_case_the_non_root_method"></a>
#### special case: the "non-root" method
This method was created later, just piggy-backing on everything that already
existed to cater to the "package" and "root" methods. In this method, the
`$GL_PACKAGE_HOOKS` is as accessible or under your control as
`$HOME/.gitolite`, so it doesn't matter where you put your hooks. I
*strongly* suggest putting them in `$GL_PACKAGE_HOOKS` and ignoring
`$HOME/.gitolite` completely.
<a name="_when_do_hooks_propagate_"></a>
### **when** do hooks propagate?
First: realise that gitolite *wants to make sure* that all the hooks in your
`hooks/common` directory get copied (symlinked, actually) to *every* repo that
gets created. **Not doing so is generally a security risk; because the
primary purpose of gitolite is access control, people generally *want* hooks
to run.**
Here's how/when hooks are created/propagated:
1. anytime you do an install, gitolite trawls through *all* existing repos
(using the unix `find` command) and force-links all the hooks in all the
repos so they all get the latest and greatest hooks.
2. anytime you do a "compile" (meaning push changes to the admin repo),
gitolite looks through all the repos named in the config. It first checks
if the repo exists, creating it if needed. It then looks for a sentinel
file called "gitolite-hooked" (an empty file in the hooks directory). If
it doesn't find it, it will assume that hooks need to be propagated.
This is because people often copy a repo from elsewhere, add it to the
config, and expect things to work. Without this step, those repos don't
get the hooks, which is bad -- the access control would have failed
silently!
3. anytime a new repo is created, the same force-linking of hooks happens.
The 3 places a new repo is created are:
* the "compile" case mentioned above, where the admin added a normal
repo to the config and pushed
* the wildrepos case, where you have "C" permissions and the repo does
not already exist
* the `fork` command in `contrib/adc`. In this case the hooks are
explicitly copied from the source repo using the `cp` command, not
using the code internal to gitolite.
For people who do not want certain hooks to run for certain repos, one simple
solution that will work right now is to check the value of `$GL_REPO` at the
start of the hook, and `exit 0` based on what it contains/matches.
[0inst]: http://sitaramc.github.com/gitolite/doc/1-INSTALL.html

View file

@ -1,221 +0,0 @@
# 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).
In this document:
* <a href="#_WARNINGS_plus_stuff_I_need_help_with">WARNINGS, plus stuff I need help with</a>
* <a href="#_additional_requirements">additional requirements</a>
* <a href="#_detailed_instructions">detailed instructions</a>
* <a href="#_install_gitolite_under_apache_">install gitolite under "apache"</a>
* <a href="#_setup_apache">setup apache</a>
* <a href="#_usage">usage</a>
* <a href="#_allowing_anonymous_access">allowing anonymous access</a>
* <a href="#_ssh_http_access_and_the_GIT_HTTP_EXPORT_ALL_variable">ssh + http access and the `GIT_HTTP_EXPORT_ALL` variable</a>
----
<a name="_WARNINGS_plus_stuff_I_need_help_with"></a>
### WARNINGS, plus stuff I need help with
* I have NOT converted the test suite to use this mode. Volunteers to
convert it to http access are welcome :-)
* I have no idea how to handle the password issue other than creating a
`~/.netrc` file and making it `chmod 600`. Anyway, http based access is
inherently less secure than pubkeys so not much point worrying about it.
* I have not tested any of the ancillary standalone programs (like
gl-dont-panic) in this mode. They're most likely going to crash and burn
because `$HOME` is not defined or in the wrong place; manually set
`HOME=$GITOLITE_HTTP_HOME` and hope for the best. Luckily most of them
have to do with sshkeys so this may not matter. YMMV.
* tested on stock Fedora 14; if you test on other environments please let me
know how it worked out and if we need to adjust this document
* tested https with dummy certs and `GIT_SSL_NO_VERIFY`; no reason why it
shouldn't work on a proper setup with everything in place
* have not tried making repos available to both ssh *and* http mode clients;
(I'd guess it ought to work fine if the "apache" user was made login-able
and given a proper $HOME and `~/.ssh/authorized_keys` and all that). If
anyone has the energy to try that please let me know how that went.
<a name="_additional_requirements"></a>
### additional requirements
* requires `GIT_PROJECT_ROOT` (see "man git-http-backend" for what this is)
set explicitly (i.e., it is no longer optional). Please set it to some
place outside apache's `DOCUMENT_ROOT`.
<a name="_detailed_instructions"></a>
### detailed instructions
I assume you've installed apache 2.x and git on the server.
I assume your httpd runs under the "apache" userid; adjust instructions below
if it does not. Similarly for "/var/www" and other file names/locations.
<a name="_install_gitolite_under_apache_"></a>
#### install gitolite under "apache"
Follow the "non-root" method, but since you can't even "su - apache", make the
following variations when doing this as root:
* `cd ~apache` first; this is `/var/www` on Fedora 14
* do this in the shell
mkdir gitolite-home
export GITOLITE_HTTP_HOME
GITOLITE_HTTP_HOME=/var/www/gitolite-home
PATH=$PATH:$GITOLITE_HTTP_HOME/bin
* now run the following commands. These are really the first 3 steps of the
"non-root" install (clone, mkdir, and gl-system-install), **except** you
substitute `GITOLITE_HTTP_HOME` in place of `HOME`. Note that you do NOT
run the gl-setup step yet.
cd gitolite-home
git clone /tmp/gitolite.git gitolite-source
# or wherever your local clone is, or directly from git://github.com/sitaramc/gitolite
cd gitolite-source
GHH=$GITOLITE_HTTP_HOME # just for convenience in next 2 commands
mkdir -p $GHH/bin $GHH/share/gitolite/conf $GHH/share/gitolite/hooks
src/gl-system-install $GHH/bin $GHH/share/gitolite/conf $GHH/share/gitolite/hooks
* after the gl-system-install step, add these to the **top** of
/var/www/gitolite-home/share/gitolite/conf/example.gitolite.rc
$ENV{GIT_HTTP_BACKEND} = "/usr/libexec/git-core/git-http-backend";
# or wherever you have that file; note: NO trailing slash
$ENV{PATH} .= ":$ENV{GITOLITE_HTTP_HOME}/bin";
# note the ".=" here, not "="
* run gl-setup with the name of your admin user
gl-setup sitaram
* IMPORTANT: fix up ownerships
chown -R apache.apache $GITOLITE_HTTP_HOME
<a name="_setup_apache"></a>
#### setup apache
You will need to setup certain values in the httpd conf, as given in `man
git-http-backend`. You can put all them into, for instance,
`/etc/httpd/conf.d/gitolite.conf` and apache [at least on Fedora 14] will pick
it up. These are the values to use; note that these are somewhat different
from those in the manpage cited above, plus we have one extra variable:
SetEnv GIT_PROJECT_ROOT /var/www/gitolite-home/repositories
SetEnv GIT_HTTP_EXPORT_ALL
# please see notes below on ssh+http access
ScriptAlias /git/ /var/www/gitolite-home/bin/gl-auth-command/
# note trailing slash
SetEnv GITOLITE_HTTP_HOME /var/www/gitolite-home
<Location /git>
AuthType Basic
AuthName "Private Git Access"
Require valid-user
AuthUserFile /path/to/some/passwdfile
</Location>
Now create/update the password file in `/path/to/some/passwdfile` using the
`htpasswd` command, and you're all done for the setup!
<a name="_usage"></a>
### usage
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
It gets even more interesting for the `setperms` command, which expects STDIN.
I didn't want to get too much into the code here, so I found that the
following works and I'm leaving it at that:
(echo R user1 user2; echo RW user3 user4) |
curl --data-binary @- http://user:password@server/git/setperms?reponame.git
With a few nice shell aliases, you won't even notice the horrible convolutions
here ;-)
<a name="_allowing_anonymous_access"></a>
### allowing anonymous access
Like [mob branches][mob] with ssh, you can allow completely
**un**-authenticated users to still have some rights specified in gitolite.
Briefly, here's how:
* specify a ScriptAlias in apache config for unauthenticated access also. I
prefer something like
ScriptAlias /gitmob/ /var/www/gitolite-home/bin/gl-auth-command/
* set `$GL_HTTP_ANON_USER` to some name, like 'mob' or 'anon' in the rc file
* give rights to this user ('mob' or 'anon' or whatever you used) in the
gitolite config file and push the change
URLs (in this example) will then look like `http://server/gitmob/reponame.git`
-- we lose the userid:passwd part and change 'git' to 'gitmob'.
<a name="_ssh_http_access_and_the_GIT_HTTP_EXPORT_ALL_variable"></a>
### ssh + http access and the `GIT_HTTP_EXPORT_ALL` variable
This document only talks about setting up access to a set of git repositories
purely via smart http. The `GIT_HTTP_EXPORT_ALL` variable must be set for
such environments.
However, it is possible to allow both ssh as well as http access, perhaps
using suexec to make the CGI run under the 'git' user [detailed documentation
patches welcome!] For those environments, this variable is not mandatory.
If you omit that variable, you can decide which repo is accessible via http by
setting `R = daemon` just for those repos.
Please note that there is no way to use "deny" rules for *read* access. Do
not try:
repo gitolite-admin
- = daemon
repo @all
R = daemon
to achieve the (possibly common) need for disallowing http access to the admin
repo.
----
Enjoy!
[mob]: http://sitaramc.github.com/gitolite/doc/mob-branches.html

View file

@ -1,279 +0,0 @@
# gitolite install transcript
In this document:
* <a href="#_about_this_document">about this document</a>
* <a href="#_create_userids_on_server_and_client_optional_">create userids on server and client (optional)</a>
* <a href="#_get_pubkey_access_from_client_to_server">get pubkey access from client to server</a>
* <a href="#_get_gitolite_source">get gitolite source</a>
* <a href="#_install_gitolite">install gitolite</a>
* <a href="#_VERY_IMPORTANT_">VERY IMPORTANT...</a>
* <a href="#_examine_what_you_have">examine what you have</a>
* <a href="#_emergency_password_access">emergency password access</a>
----
<a name="_about_this_document"></a>
### about this document
This is a *complete* transcript of a full gitolite install, *from scratch*,
using brand new userids ("sita" on the client, "git" on the server). Please
note that you can use existing userids also, it is not necessary to use
dedicated user IDs for this. In particular, people who have a single user
hosting account can also use this method, as long as they have password access
as a fallback if they screw up the keys somewhere. Also, you don't have to
use some *other* server for all this, both server and client can be
"localhost" if you like.
Please note that this entire transcript can be summarised as:
* create users on client and server (optional)
* get pubkey access to server from client (`ssh-copy-id` or manual eqvt)
* run one command ***on client*** (`gl-easy-install`)
...and only that last step is actually gitolite. In fact, the bulk of the
transcript is **non**-gitolite stuff :)
**Please also note that this method will setup everything on the server, but
you have to run it on your workstation, NOT on the server!**
----
<a name="_create_userids_on_server_and_client_optional_"></a>
### create userids on server and client (optional)
Client side: add user, give him a password
sita-lt:~ # useradd sita
sita-lt:~ # passwd sita
Changing password for user sita.
New UNIX password:
Retype new UNIX password:
passwd: all authentication tokens updated successfully.
Server side: (log on to server, then) add user, give it a password
sita-lt:~ # ssh sitaram@server
sitaram@server's password:
Last login: Fri Dec 18 20:25:06 2009
-bash-3.2$ su -
Password:
sita-sv:~ # useradd git
sita-sv:~ # passwd git
Changing password for user git.
New UNIX password:
Retype new UNIX password:
passwd: all authentication tokens updated successfully.
Server side: allow ssh access to "git" user
This is done by editing the sshd config file and adding "git" to the
"AllowUsers" list (the grep command is just confirming the change we made,
because I'm not showing the actual "vi" session):
sita-sv:~ # vim /etc/ssh/sshd_config
sita-sv:~ # grep -i allowusers /etc/ssh/sshd_config
AllowUsers sitaram git
sita-sv:~ # service sshd restart
Stopping sshd: [ OK ]
Starting sshd: [ OK ]
**NOTE**: if the `AllowUsers` setting is completely missing from the sshd
config file, all users are allowed (see `man sshd_config`). You may prefer to
leave it that way -- your choice. I prefer to make the usernames explicit
because I'm paranoid ;-)
----
<a name="_get_pubkey_access_from_client_to_server"></a>
### get pubkey access from client to server
This involves creating a keypair for yourself (using `ssh-keygen`), and
copying the public part of that keypair to the `~/.ssh/authorized_keys` file
on the server (using `ssh-copy-id`, if you're on Linux, or the manual method
described in the `ssh-copy-id` section in `doc/3-faq-tips-etc.mkd`).
sita-lt:~ $ su - sita
Password:
sita@sita-lt:~ $ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/sita/.ssh/id_rsa):
Created directory '/home/sita/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/sita/.ssh/id_rsa.
Your public key has been saved in /home/sita/.ssh/id_rsa.pub.
The key fingerprint is:
8a:e0:60:1b:04:58:68:50:a4:d7:d0:3a:a5:2d:bf:0a sita@sita-lt.atc.tcs.com
The key's randomart image is:
+--[ RSA 2048]----+
|===. |
|+o oo |
|o..=. |
|..= . |
|.o.+ S |
|.oo... . |
|E.. ... |
| . . |
| .. |
+-----------------+
sita@sita-lt:~ $ ssh-copy-id -i ~/.ssh/id_rsa.pub git@server
git@server's password:
/usr/bin/xauth: creating new authority file /home/git/.Xauthority
Now try logging into the machine, with "ssh 'git@server'", and check in:
.ssh/authorized_keys
to make sure we haven't added extra keys that you weren't expecting.
Double check to make sure you can log on to `git@server` without a password:
sita@sita-lt:~ $ ssh git@server pwd
/home/git
**DO NOT PROCEED UNTIL THIS WORKS OK!**
----
<a name="_get_gitolite_source"></a>
### get gitolite source
sita@sita-lt:~ $ git clone git://github.com/sitaramc/gitolite gitolite-source
Initialized empty Git repository in /home/sita/gitolite-source/.git/
remote: Counting objects: 1157, done.
remote: Compressing objects: 100% (584/584), done.
remote: Total 1157 (delta 756), reused 912 (delta 562)
Receiving objects: 100% (1157/1157), 270.08 KiB | 61 KiB/s, done.
Resolving deltas: 100% (756/756), done.
<a name="_install_gitolite"></a>
### install gitolite
Note that gitolite is installed from the *client*. The `easy-install` script
runs on the client but installs gitolite on the server!
sita@sita-lt:~ $ cd gitolite-source/src
<font color="red"> **This is the only gitolite specific command in a typical
install sequence**. </font> Run it without any arguments to see a usage
message. Run it without the `-q` to get a more verbose, pause-at-every-step,
install mode that allows you to change the defaults (for example, if you want
a different UMASK setting, or you want the repos to be in a different place,
etc.)
sita@sita-lt:src $ ./gl-easy-install -q git server sitaram
you are upgrading (or installing first-time) to v0.95-38-gb0ce84d
setting up keypair...
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/sita/.ssh/sitaram.
Your public key has been saved in /home/sita/.ssh/sitaram.pub.
The key fingerprint is:
2a:8e:88:42:36:7e:71:e8:cc:ff:4c:54:64:8e:cf:19 sita@sita-lt.atc.tcs.com
The key's randomart image is:
+--[ RSA 2048]----+
| o |
| = |
| . E |
| + o |
| . .S+ |
| + o ... |
|+ = + .. |
|oo B .o |
|+ o o..o |
+-----------------+
creating gitolite para in ~/.ssh/config...
finding/creating gitolite rc...
installing/upgrading...
Initialized empty Git repository in /home/git/repositories/gitolite-admin.git/
Initialized empty Git repository in /home/git/repositories/testing.git/
Pseudo-terminal will not be allocated because stdin is not a terminal.
fatal: No HEAD commit to compare with (yet)
[master (root-commit) 2f40d4b] start
2 files changed, 13 insertions(+), 0 deletions(-)
create mode 100644 conf/gitolite.conf
create mode 100644 keydir/sitaram.pub
cloning gitolite-admin repo...
Initialized empty Git repository in /home/sita/gitolite-admin/.git/
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (6/6), done.
---------------------------------------------------------------
done!
Reminder:
*Your* URL for cloning any repo on this server will be
gitolite:reponame.git
*Other* users you set up will have to use
git@server:reponame.git
If this is your first time installing gitolite, please also:
tail -31 ./gl-easy-install
for next steps.
----
<a name="_VERY_IMPORTANT_"></a>
### VERY IMPORTANT...
Please read the text that the easy-install command produces as output when you
run it. People who fail to read this get into trouble later. And I didn't
write all that because I wanted to practice typing.
The text just above this section is an approximation; your version will
contain the correct URLs for your install, including port numbers if
non-standard ports were used).
Try out that `tail -31 ./gl-easy-install` too :)
<a name="_examine_what_you_have"></a>
### examine what you have
The last step of the previous command creates a local clone of your
gitolite-admin repo in `~/gitolite-admin`.
sita@sita-lt:src $ cd ~/gitolite-admin/
sita@sita-lt:gitolite-admin $ git --no-pager log --stat
commit 2f40d4bb80d424dc39aae5d0973f8c1b2e395666
Author: git <git@sita-lt.atc.tcs.com>
Date: Thu Dec 24 21:39:15 2009 +0530
start
conf/gitolite.conf | 12 ++++++++++++
keydir/sitaram.pub | 1 +
2 files changed, 13 insertions(+), 0 deletions(-)
And that's really all. Add keys to keydir here, edit conf/gitolite.conf as
needed, then add, commit, and push the changes to the server.
<a name="_emergency_password_access"></a>
### emergency password access
If you lose your keys or the worst happens and you use the wrong key for the
wrong thing and apparently lose all access, but you still know the password,
this is what you do:
sita@sita-lt:~ $ ssh -o preferredauthentications=password git@server
git@server's password:

View file

@ -1,109 +0,0 @@
# migrating from gitosis to gitolite
[TODO: make the migration tool fix up gitweb and daemon control also...]
Migrating from gitosis to gitolite is pretty easy, because the basic design is
the same.
Here's how we migrated my work repos:
1. login as the `git` user on the server, and get a bash shell prompt
2. **disable gitosis** by renaming `/usr/bin/gitosis-serve` to something
else. This will prevent users from pushing anything while you do the
backup, migration, etc.
3. **edit** `~/.ssh/authorized_keys` and **carefully** remove all the lines
containing "gitosis-serve", as well as the marker line that says
"auto-generated by gitosis, DO NOT REMOVE", then save the file. If the
file did not have any other keys and is now empty, don't worry -- save it
anyway because gitolite expects the file to be present (even if it is
empty).
4. For added safety, **delete** the post-update hook that gitosis-admin
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`.
If you do not do this, an accidental push to the gitosis-admin repo will
mess up your `~/.ssh/authorized_keys` file
5. If you already use the `update` hook for some reason, **rename** it (on
each individual repository) to `update.secondary`. This is because
gitolite uses the update hook for checking write access.
6. take a **backup** of the `~/repositories` directory
Now, log off the server and get back to the client:
1. follow instructions to install gitolite; see the [install document][inst].
Make sure that you **don't** change the default path for `$REPO_BASE` if
you edit the config file!
This will give you a gitolite config that has the required entries for the
"gitolite-admin" repo.
2. **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`
src/gl-conf-convert < $GSAC/gitosis.conf >> $GLAC/gitolite.conf
Be sure to check the file to make sure it converted correctly
3. **copy** the keys from gitosis's keydir (same meanings for GSAC and GLAC)
cp $GSAC/keydir/* $GLAC/keydir
4. **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.
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.
[This][mk] will tell you more about these nuances.
5. **IMPORTANT: expand any multi-key files you may have**. [Here][mk]'s an
explanation of what multi-keys are, how gitosis does them and how gitolite
does it differently.
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*
6. Check all your changes to your gitolite-admin clone, commit, and push
[mk]: http://sitaramc.github.com/gitolite/doc/3-faq-tips-etc.html#multikeys
[inst]: http://sitaramc.github.com/gitolite/doc/1-INSTALL.html

View file

@ -1,346 +0,0 @@
## mirroring a gitolite setup
Mirroring git repos is essentially a one-liner. For each mirror you want to
update, you just add a post-receive hook that says
#!/bin/bash
git push --mirror slave_user@mirror.host:/path/to/repo.git
But life is never that simple...
**This document has been tested using a 3-server setup, all installed using
the "non-root" method (see doc/1-INSTALL.mkd). However, the process is
probably not going to be very forgiving of human error -- like anything that
is this deep in "system admin" territory, errors are likely to be costly. If
you're the kind who hits enter first and then thinks about what he typed,
you're in for some fun times ;-)**
**On the plus side, everything we do is done using git commands, so things are
never *really* lost until you do a `git gc`**.
----
**Update 2011-03-10**: I wrote this with a typical "corporate" setup in mind
where all the servers involved are owned and administered by the same group of
people. As a result, the scripts assume the servers trust each other
completely. If that is not your situation, you will have to add code into
`gl-mirror-shell` to limit the commands the remote may send. Patches welcome
:-)
----
In this document:
* <a href="#_RULE_NUMBER_ONE_">RULE NUMBER ONE!</a>
* <a href="#_things_that_will_NOT_be_mirrored_by_this_process">things that will NOT be mirrored by this process</a>
* <a href="#_conventions_in_this_document">conventions in this document</a>
* <a href="#_setting_up_mirroring">setting up mirroring</a>
* <a href="#_install_gitolite_on_all_servers">install gitolite on all servers</a>
* <a href="#_generate_keypairs">generate keypairs</a>
* <a href="#_setup_the_mirror_shell_on_each_server">setup the mirror-shell on each server</a>
* <a href="#_set_slaves_to_slave_mode">set slaves to slave mode</a>
* <a href="#_set_slave_server_lists">set slave server lists</a>
* <a href="#_efficiency_versus_paranoia">efficiency versus paranoia</a>
* <a href="#_syncing_the_mirrors_the_first_time">syncing the mirrors the first time</a>
* <a href="#_switching_over">switching over</a>
* <a href="#_the_return_of_foo">the return of foo</a>
* <a href="#_switching_back">switching back</a>
* <a href="#_making_foo_a_slave">making foo a slave</a>
* <a href="#_URLs_that_your_users_will_use">URLs that your users will use</a>
<a name="_RULE_NUMBER_ONE_"></a>
### RULE NUMBER ONE!
**RULE OF GIT MIRRORING: users should push directly to only one server**! All
the other machines (the slaves) should be updated by the master server.
If a user pushes directly to one of the slaves, those changes will get wiped
out on the next mirror push from the real master server.
Corollary: if the primary went down and you effected a changeover, you must
make sure that the primary does not come up in a push-enabled mode when it
recovers.
<a name="_things_that_will_NOT_be_mirrored_by_this_process"></a>
### things that will NOT be mirrored by this process
Let's get this out of the way. This procedure will only mirror your git
repositories, using `git push --mirror`. Therefore, certain files will not be
mirrored:
* gitolite log files
* "gl-creator" and "gl-perms" files
* "projects.list", "description", and entries in the "config" files within
each repo
None of these affect actual repo contents of course, but they could be
important, (especially the gl-creator, although if your wildcard pattern had
"CREATOR" in it you can recreate those files easily enough anyway).
Your best bet is to use rsync for the log files, and tar for the others, at
regular intervals.
<a name="_conventions_in_this_document"></a>
### conventions in this document
The userid hosting gitolite is `gitolite` on all machines. The servers are
foo, bar, and baz. At the beginning, foo is the master, the other 2 are
slaves.
<a name="_setting_up_mirroring"></a>
### setting up mirroring
<a name="_install_gitolite_on_all_servers"></a>
#### install gitolite on all servers
* before running the final step in the install sequence, make sure you go to
the `hooks/common` directory and rename `post-receive.mirrorpush` to
`post-receive`. See doc/hook-propagation.mkd if you're not sure where you
should look for `hooks/common`.
* if the server already has gitolite installed, use the normal methods to
make sure this hook gets in.
* Use the same "admin key" on all the machines, so that the same person has
gitolite-admin access to all of them.
<a name="_generate_keypairs"></a>
#### generate keypairs
Each server will be potentially logging on to one or more of the other
servers, so first generate keypairs for all of them (`ssh-keygen`) and copy
the `.pub` files to all other servers, named appropriately. So foo will have
bar.pub and baz.pub, etc.
<a name="_setup_the_mirror_shell_on_each_server"></a>
#### setup the mirror-shell on each server
XXX review this document after testing mirroring...
If you installed gitolite using the from client method, run the following:
# on foo
export GL_BINDIR=$HOME/.gitolite/src
cat bar.pub baz.pub |
sed -e 's,^,command="'$GL_BINDIR'/gl-mirror-shell" ,' >> ~/.ssh/authorized_keys
If you installed using any of the other 3 methods do this:
# on foo
export GL_BINDIR=`gl-query-rc GL_BINDIR`
cat bar.pub baz.pub |
sed -e 's,^,command="'$GL_BINDIR'/gl-mirror-shell" ,' >> ~/.ssh/authorized_keys
Also do the same thing on the other machines.
Now test this access:
# on foo
ssh gitolite@bar pwd
# should print /home/gitolite/repositories
ssh gitolite@bar uname -a
# should print the appropriate info for that server
Similarly test the other combinations.
<a name="_set_slaves_to_slave_mode"></a>
#### set slaves to slave mode
Set slave mode on all the *slave* servers by setting `$GL_SLAVE_MODE = 1`
(uncommenting the line if necessary).
Leave the master server's file as is.
<a name="_set_slave_server_lists"></a>
#### set slave server lists
On the master (foo), set the names of the slaves by editing the
`~/.gitolite.rc` to contain:
$ENV{GL_SLAVES} = 'gitolite@bar gitolite@baz';
**Note the syntax well; this is critical**:
* **this must be in single quotes** (or you must remember to escape the `@`)
* the variable is an ENV var, not a plain perl var
* the values are *space separated*
* each value represents the userid and hostname for one server
The basic idea is that this string, should be usable in both the following
syntaxes:
git clone gitolite@bar:repo
ssh gitolite@bar pwd
You can also use ssh host aliases. Let's say server "bar" has a non-standard
port number:
# in ~/.ssh/config on foo
host mybar
hostname bar
user gitolite
port 2222
# in ~/.gitolite.rc on foo
$ENV{GL_SLAVES} = 'bar gitolite@baz';
And that's really all there is, unless...
<a name="_efficiency_versus_paranoia"></a>
### efficiency versus paranoia
If you're paranoid enough to use mirrors, you should be paranoid enough to
like the `receive.fsckObjects` setting we now default to :-) However, informal
tests indicate a 40-50% CPU overhead from this. If you don't like that,
remove that line from the post-receive code.
Please also note that we only set it on mirrors, and that too at the time the
mirrored repo is *created*. This means, when you start using your old "main"
server as a mirror (see later sections on switching over to a mirror, etc.),
it's repos do not have this setting. Repos created by previous versions of
gitolite also will not have this setting.
Personally, I just set `git config --global receive.fsckObjects true`, since
those servers aren't doing anything else anyway, and are idle for long
stretches of time. It's upto you what you want to do here.
<a name="_syncing_the_mirrors_the_first_time"></a>
### syncing the mirrors the first time
This is fine if you're setting up everything from scratch. But if your master
server already had some repos with commits on them, you have to manually sync
them up once.
# on foo
gl-mirror-sync gitolite@bar
# path to "sync" program is ~/.gitolite/src if "from-client" install
<a name="_switching_over"></a>
### switching over
Let's say foo goes down. You want to make bar the main server, and continue
to have "baz" be a slave.
* on bar, edit `~/.gitolite.rc` and set
$GL_SLAVE_MODE = 0;
$ENV{GL_SLAVES} = 'gitolite@baz';
* **sanity check**: go to your gitolite-admin clone, add a remote for "bar",
fetch it, and make sure they are the same:
git remote add bar gitolite@bar:gitolite-admin
git fetch bar
git branch -a -v
# check that all SHAs are the same
* inform everyone of the new URL for their repos (see next section for more
on this)
* make sure that if "foo" does come up, it will not immediately start
serving requests. You'll be in trouble if (a) foo comes up as it was
before, and (b) some developer still had the old URL lying around and
started pushing changes to it.
You could jump in quickly and set `$GL_SLAVE_MODE = 1` as soon as the
system comes up. Better still, use extraneous means to block incoming
connections from normal users (out of scope for this document).
<a name="_the_return_of_foo"></a>
### the return of foo
<a name="_switching_back"></a>
#### switching back
Switching back is fairly easy.
* synchronise all repos from bar to foo. This may take some time, depending
on how long foo was down.
# on bar
gl-mirror-sync gitolite@foo
# path to "sync" program is ~/.gitolite/src if "from-client" install
* turn off pushes on "bar" by setting slave mode to 1
* run the sync once again; this should complete quickly
* **double check by comparing some the repos on both sides if needed**. You
could run the following snippet on all servers for a quick check:
cd ~/repositories # or wherever $REPO_BASE is
find . -type d -name "*.git" | sort |
while read r
do
echo $r
git ls-remote $r | sort
done | md5sum
* on foo, set the slave list (or check that it is correct)
* on foo, set slave mode off
* tell everyone to switch back
<a name="_making_foo_a_slave"></a>
#### making foo a slave
If "foo" does come up in a controlled manner, you might not want to switch
back right away. Unless you're doing DNS tricks, users may be peeved at
having to do 2 switches.
If you want to make foo a slave, you know the drill by now:
* set slave mode to 1 on foo
* on bar, add foo as a slave
# in ~/.gitolite.rc on bar
$ENV{GL_SLAVES} = 'gitolite@foo gitolite@baz';
I think that should cover pretty much everything. I *have* tested most of
this, but YMMV.
----
<a name="_URLs_that_your_users_will_use"></a>
### URLs that your users will use
Unless you play DNS tricks, it is more than likely that your users would have
to change the URLs they use to access their repos if you change the server
they push to.
I cannot speak for the plethora of git client software out there but for
normal git, this problem can be mitigated somewhat by doing this:
* in `~/.ssh/config` on my workstation, I have
host gl
hostname=primary.server.ip
user=gitolite
* all my `git clone` commands use `gl:reponame` as the URL
* if the primary goes down, and I have to access the secondary, I just
change the `hostname` line in `~/.ssh/config`.
That's it. Every clone of every repo used anywhere in this userid is now
changed.
To repeat, this may or may not work with all the git clients that exist (like
jgit, or any of the GUI tools, and especially if you're on Windows).
If anyone has a better idea, something that works more universally, I'd love
to hear it.

View file

@ -1,76 +0,0 @@
## mob branches in gitolite
WARNING: This is hairy stuff. But what's life without a little danger?
WARNING 2: girocco does mob branches quite differently; the controls on what a
mob branch can do are much more fundamental. Here we just trick gitolite into
accepting anonymous ssh connections and pretending they're from a mythical
user called "mob". **This means all the access control is -- as you might
expect -- in the gitolite.conf file, so make sure you don't give the `mob`
user too many rights!**
(tested on Fedora 13; assumes your gitolite server userid is "gitolite" and
install was "from-client" method; adjust according to your environment. If
you need more than this, you should not be enabling mob branches anyway ;-)
[hah! Easy way out of being badgered with questions!]
* create a file called `/tmp/mobshell` (put it somewhere more permanent if
you wish). This file should be `chmod +x` and contain
#!/bin/sh
shift
export SSH_ORIGINAL_COMMAND
SSH_ORIGINAL_COMMAND="$*"
/home/gitolite/.gitolite/src/gl-auth-command mob
# see one of the lines in ~gitolite/.ssh/authorized_keys for the
# precise location of the gl-auth-command script
* create a user called mob. Give it the same UID number and `$HOME` as your
gitolite server userid, and set the login shell to be the script you just
created. Also delete the password.
id -u gitolite
# returns 503 or something...
useradd -d /home/gitolite -s /tmp/mobshell -u 503 -o mob
passwd -d mob
* make sure you have a recent enough sshd and put these lines at the bottom,
then restart sshd
Match user mob
PermitEmptyPasswords yes
That's it. Now you can add stuff to your gitolite.conf file. Here's some
examples:
* This allows the mob user to do anything to the "mob" branch:
repo foo
RW+ = alice bob
R = eve
RW+ mob$ = mob
# only the mob branch, nothing more
* This is the same, except it can be any branch under "mob/" so you get some
flexibility:
RW+ mob/ = mob
* Girocco allows pushing to the mob branch only if it already exists (that
is, the mob user cannot *create* the mob branch, but if it already exists
he can push to it). Here's how you'd do that in gitolite:
repo foo
RW+C = alice bob
R = eve
RW+ mob$ = mob
* This gives *every* repo a mob branch (be careful!)
repo @all
RW+ mob$ = mob
How do mob users access it? The URLs just look like: `mob@server:repo`
instead of `gitolite@server:repo` That's it!

View file

@ -1,67 +0,0 @@
## (contributed doc: integrating gitolite with monkeysphere)
This document attempts to describe one way to integrate
[Monkeysphere](http://web.monkeysphere.info/) authentication
with [gitolite](http://github.com/sitaramc/gitolite).
We presuppose that you have a system with a new enough
version of Monkeysphere to support ssh `authorized_keys`
options, and that you are not making use of
monkeysphere-authentication on this system.
As a first step, import the key or keys you wish to
act as Monkeysphere certifiers into the GnuPG public
keyring of the gitolite user (for example,
`gpg --keyserver pool.sks-keyservers.net --recv-keys B0AE9A02`)
Then edit such keys (`gpg --edit B0AE9A02`) and assign them
*ultimate* ownertrust.
Next install a script of this nature as `post-update.secondary`
in the `hooks/` directory of the `gitolite-admin` repository. You can also
follow the "using hooks" section in gitolite's "admin" document to let
gitolite put your new hook in the correct place.
#!/bin/zsh
# this should use locking
pushd ${GL_ADMINDIR}
if [[ -d monkeydir ]]
then
cp ~/.monkeysphere/authorized_user_ids ~/.monkeysphere/old-authorized_user_ids
rm -f ~/.monkeysphere/new-authorized_user_ids
for i in monkeydir/*.pub
do
username=$i:t:r
for j in ${(f)"$(<$i)"}
do
cat >> ~/.monkeysphere/new-authorized_user_ids <<EOF
$j
command="/usr/share/gitolite/gl-auth-command $username"
no-port-forwarding
no-X11-forwarding
no-agent-forwarding
no-pty
EOF
done
done
mv ~/.monkeysphere/new-authorized_user_ids ~/.monkeysphere/authorized_user_ids
monkeysphere update-authorized_keys
fi
popd
ADMIN_POST_UPDATE_CHAINS_TO=hooks/post-update.tertiary
if [[ -f $ADMIN_POST_UPDATE_CHAINS_TO || -L $ADMIN_POST_UPDATE_CHAINS_TO ]]; then
exec $ADMIN_POST_UPDATE_CHAINS_TO "$@"
fi
Finally, place *username*.pub files containing OpenPGP IDs into
a directory called `monkeydir/` in the root of the gitolite-admin
repository. If everything has been set up correctly, adding
and pushing these files should then result in the appropriate
generation of `~/.ssh/authorized_keys`.

View file

@ -1,57 +0,0 @@
## when gitolite is overkill
Note: I wrote this to help people for whom gitolite is genuinely overkill. I
believe it will all work, but YMMV.
----
You don't always need something like gitolite. If you have a fixed (or very
rarely changing) number of users, and all of them have full access to all your
repos, you can use plain Unix permissions to get a lot of this done:
* dedicate a userid (say "git") to host all your repos. This user will also
have a group (normally called "git" on most distros I think)
* create a directory that is accessible (at least "r" and "x" permissions)
to the group "git", all the way upto the root. (That is, if the directory
you chose is /home/git/repos, then /, /home, /home/git, and
/home/git/repos must all be "g+rx").
* create all repos in this directory, as the "git" user, using the following
command:
git init --bare --shared reponame.git
* For each user who needs access to the repos, add them as members to the
"git" group also. On Fedora this is:
usermod -a -G git username
And that's basically it. The "init --shared" will create the repos with
"chmod -R g+s". If you have existing repos where you forgot (or didn't know)
the "--shared" argument, do this on each of them:
cd reponame.git
git init --shared --bare
chmod -R g+w .
chmod g+s `find . -type d`
I think that should do it.
Once you've setup the Unix level permissions, you may consider setting the
shell of some of the less experienced users to "git-shell" (using its full
path) if they don't really need a shell on the server. This will let them
access git remotely but not do anything else.
Combining this with settings like `receive.denyDeletes` and
`receive.denyNonFastForwards`, or at least `core.logAllRefUpdates`, can go a
long way toward preventing accidents or at least making it feasible to recover
from them.
----
You can do more complex things using Unix acls. If you do, and feel like
writing it up, send it to me and I will add it here (with credit given of
course). Personally, I can't be bothered -- once you have differing needs for
different people, you really need gitolite anyway, because you probably need
different rights for branches as well and Unix ACLs can't do that.

View file

@ -1,56 +0,0 @@
## packaging gitolite
Here's how you'd package gitolite. In the following description, location "X"
can be, say, `/usr/share/gitolite/conf` or some such, and similarly location
"Y" can be perhaps `/usr/share/gitolite/hooks`. It's upto your distro
policies where they are.
**Step 1**: Clone the gitolite repo and run the make command inside the clone
git clone git://github.com/sitaramc/gitolite.git
cd gitolite
make pu.tar # or "make master.tar" or "make v1.2.tar" etc
Then you explode the tar file in some temporary location.
*Alternatively, you can `git checkout` the tag or branch you want, and run
this command in the clone directly*:
git describe --tags --long > conf/VERSION
**Step 2**: Now make the following changes (no trailing slashes in the
location values please):
* `src/gl-setup` should have the following line:
GL_PACKAGE_CONF="X"
* `conf/example.gitolite.rc` should have the following lines:
$GL_PACKAGE_CONF="X";
$GL_PACKAGE_HOOKS="Y";
* delete `src/gl-easy-install`; that script is meant for a totally different
mode of installation and does *not* play well in this mode :-)
**Step 3**: Move (or arrange to move) the files to their proper locations as
given below:
* everything in "src" goes somewhere on the PATH
* everything in "conf" goes to location "X"
* everything in "hooks" goes to location "Y"
**Step 4**: There is no step 4. Unless you count telling your users to run
`gl-setup` as a step :)
On the initial install (urpmi, yum install, or apt-get install), you could
also choose to setup a userid called "gitolite", and run "gl-setup" as that
user; however I do not know how you would come up with the initial pubkey that
is needed. Anyway, the point is that the "gitolite" user is no more special
than any other in terms of hosting gitolite. Any user can host gitolite on
his userid by just running "gl-setup".
When you upgrade, just overwrite all the files; it'll all just work. In fact,
other than the initial "gl-setup" run, the only time a gitolite hosting user
has to actually do anything is to edit their own `~/.gitolite.rc` file if they
want to enable or disable specific features.

View file

@ -1,190 +0,0 @@
# (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][gldpg]. 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.
[gldpg]: http://sitaramc.github.com/gitolite/doc/progit-article.html
Git has started to 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.
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.
<a name="_Installing_"></a>
### 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; various Linux flavours, and Solaris 10, have been tested. 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 `gitolite` 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". As a result, there is a notion of "installing" the software itself, and then "setting up" a user as a "gitolite host".
Gitolite has 4 methods of installation. People using Fedora or Debian systems can obtain an RPM or a DEB and install that. People with root access can install it manually. In these two methods, any user on the system can then become a "gitolite host".
People without root access can install it within their own userids. And finally, gitolite can be installed by running a script *on the workstation*, from a bash shell. (Even the bash that comes with msysgit will do, in case you're wondering.)
We will describe this last method in this article; for the other methods please see the documentation.
You start by obtaining public key based access to your server, so that you can log in from your workstation to the server without getting a password prompt. The following method works on Linux; for other workstation OSs you may have to do this manually. We assume you already had a key pair generated using `ssh-keygen`.
$ ssh-copy-id -i ~/.ssh/id_rsa.pub gitolite@gitserver
This will ask you for the password to the gitolite account, and then set up public key access. This is **essential** for the install script, so check to make sure you can run a command without getting a password prompt:
$ ssh gitolite@gitserver pwd
/home/gitolite
Next, you clone Gitolite from the project's main site and run the "easy install" script (the third argument is your name as you would like it to appear in the resulting gitolite-admin repository):
$ git clone git://github.com/sitaramc/gitolite
$ cd gitolite/src
$ ./gl-easy-install -q gitolite gitserver sitaram
And you're done! Gitolite has now been installed on the server, and you now have a brand new repository called `gitolite-admin` in the home directory of your workstation. You administer your gitolite setup by making changes to this repository and pushing.
That last command does produce a fair amount of output, which might be interesting to read. Also, the first time you run this, a new keypair is created; you will have to choose a passphrase or hit enter for none. Why a second keypair is needed, and how it is used, is explained in the "ssh troubleshooting" document that comes with Gitolite. (Hey the documentation has to be good for *something*!)
<a name="_Customising_the_Install_"></a>
### Customising the Install ###
While the default, quick, install works for most people, there are some ways to customise the install if you need to. If you omit the `-q` argument, you get a "verbose" mode install -- detailed information on what the install is doing at each step. The verbose mode also allows you to change certain server-side parameters, such as the location of the actual repositories, by editing an "rc" file that the server uses. This "rc" file is documented in [doc/gitolite.rc.mkd][rcdoc] so you should be able to make any changes you need quite easily, save it, and continue. This file also contains various settings that you can change to enable or disable some of gitolite's advanced features.
<a name="_Config_File_and_Access_Control_Rules_"></a>
### 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
#gitolite conf
# please see doc/gitolite.conf.mkd for details on syntax and features
repo gitolite-admin
RW+ = sitaram
repo testing
RW+ = @all
Notice that "sitaram" (the last argument in the `gl-easy-install` command you gave earlier) has read-write permissions on the `gitolite-admin` repository as well as a public key file of the same name.
The config file syntax for gitolite is documented in [doc/gitolite.conf.mkd][confdoc] 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.
<a name="_Advanced_Access_Control_with_deny_rules_"></a>
### 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.
<a name="_Restricting_pushes_by_files_changed_"></a>
### 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
RW NAME/ = @senior_devs
- NAME/Makefile = @junior_devs
RW NAME/ = @junior_devs
This powerful feature is documented in `conf/example.conf`.
<a name="_Personal_Branches_"></a>
### 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>/*`); see the "personal branches" section in `doc/3-faq-tips-etc.mkd` for details.
<a name="_Wildcard_repositories_"></a>
### "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. This is a *very* powerful feature, which has to be enabled by setting `$GL_WILDREPOS = 1;` in the rc file. 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 in `doc/wildcard-repositories.mkd`.
<a name="_Other_Features_"></a>
### 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 "faqs, tips, etc" and other documents.
**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.
**Git outside normal PATH**: One extremely useful convenience feature in gitolite is support for git installed outside the normal `$PATH` (this is more common than you think; some corporate environments or even some hosting providers refuse to install things system-wide and you end up putting them in your own directories). Normally, you are forced to make the *client-side* git aware of this non-standard location of the git binaries in some way. With gitolite, just choose a verbose install and set `$GIT_PATH` in the "rc" files. No client-side changes are required after that :-)
**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, the gitolite version here is v1.5.4-19-ga3397d4
the gitolite config gives you the following access:
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. This feature has its own documentation file in the `doc/` directory.
**Gitweb support**: Gitolite supports gitweb in several ways. You can specify which repos are visible via gitweb. You can set the "owner" and "description" for gitweb from the gitolite config file. Gitweb has a mechanism for you to implement access control based on HTTP authentication, so you can make it use the "compiled" config file that gitolite produces, which means the same access control rules (for read access) apply for gitweb and gitolite.
**Mirroring**: Gitolite can help you maintain multiple mirrors, and switch between them easily if the primary server goes down.
[rcdoc]:http://sitaramc.github.com/gitolite/doc/gitolite.rc.html
[confdoc]:http://sitaramc.github.com/gitolite/doc/gitolite.conf.html

View file

@ -1,149 +0,0 @@
# output of the "info" and "expand" commands
Running "ssh git@server info" or "ssh git@server expand" gives you certain
output. This doclet describes the output; you're welcome to help me make it
clearer :)
(Side note: if you installed using the "from-client" method, and you're the
administrator, please replace `ssh git@server` with `ssh gitolite`, all
through this document).
In this document:
* <a href="#_the_info_command">the "info" command</a>
* <a href="#_interpreting_the_output">interpreting the output</a>
* <a href="#_using_patterns_to_limit_output">using patterns to limit output</a>
* <a href="#_side_note_openssh_5_6">side note: openssh 5.6</a>
* <a href="#_the_expand_command">the "expand" command</a>
----
<a name="_the_info_command"></a>
### the "info" command
Usage:
ssh git@server info [optional_pattern [list of users]]
The "info" command shows you all the repos (and repo patterns) in the config
file that you have been given any kind of access to. If you supply an
optional pattern the output will be limited to repos matching that pattern.
If you're an admin you can append a list of users to see their permissions
instead of your own; in this mode the pattern is mandatory, even if you just
use `.` to cheat.
Here is a sample output of the info command. There are 3 columns of
permissions (create, read, and write) in the output, although the first column
is often blank.
$ ssh git@server info
hello sitaram, the gitolite version here is v1.5.5-24-g2b066fc
the gitolite config gives you the following access:
R W SecureBrowse
R W anu-wsd
R W entrans
@R W git-notes
@R W gitolite
R W gitolite-admin
R W indic_web_input
@C R W private/sitaram/[\w.-]+
R W proxy
@C @R W public/sitaram/[\w.-]+
@R_ @W_ testing
R W vkc
<a name="_interpreting_the_output"></a>
#### interpreting the output
The meaning of C, R, and W are self-explanatory, but they may be prefixed or
suffixed by a symbol:
* an `@` prefix means "@all" users have been given this permission
repo foo
R = @all
* a `#` prefix means this user is a "superuser" (think root's shell prompt)
and so has access to `@all` repos. Which means you'll see this prefix
(or, in some cases, an `&`; see next bullet) for *all* the repos, or none
of them
repo @all
R = sitaram
* an `&` prefix means both of the above are true
The `_` suffix is special. This says the user has only implicit access (due
to one of the `@all` uses), but no explicit access.
<a name="_using_patterns_to_limit_output"></a>
#### using patterns to limit output
Here are a couple of samples with optional patterns:
$ ssh git@server info git
hello sitaram, the gitolite version here is v1.5.5-24-g2b066fc
the gitolite config gives you the following access:
@R W git-notes
@R W gitolite
R W gitolite-admin
$ ssh git@server info admin
hello sitaram, the gitolite version here is v1.5.5-24-g2b066fc
the gitolite config gives you the following access:
R W gitolite-admin
In "big-config" mode (i.e., when `GL_BIG_CONFIG` is set) the pattern is
**mandatory**. You can try and cheat the system by passing in a "." but
gitolite truncates the output after 20 results to prevent a DOS. (This limit
can be changed; see `$BIG_INFO_CAP` in [doc/gitolite.rc.mkd][rcdoc]).
[rcdoc]: http://sitaramc.github.com/gitolite/doc/gitolite.rc.html
The pattern is also mandatory when an admin wants to find out what access some
*other* user has, which you may have guessed from the syntax in the "usage"
line above.
<a name="_side_note_openssh_5_6"></a>
#### side note: openssh 5.6
It used to be that the gitolite documentation would say "just use `ssh
git@server`" in the past, because gitolite defaults to the "info" command if
no command is passed.
However, starting with [openssh 5.6][openssh56], this won't work. Openssh
will now "Kill channel when pty allocation requests fail". This means that
gitolite is not even invoked; you only get a message about pty allocation
failure, followed by "connection closed".
So now you have to use an explicit "info" command, (`ssh git@server info`) or
add the `-T` option to ssh (`ssh -T git@server`).
[openssh56]: http://www.openssh.org/txt/release-5.6
<a name="_the_expand_command"></a>
### the "expand" command
Usage:
ssh git@server expand [optional_pattern]
The "expand" command trawls through all the repositories on the server,
limiting to repos matching the pattern you provide (default is all repos
found).
For each repo found, it searches for it in the config -- either the actual
repo entry (when the repo is not a wildcard repo), or an entry for the
wildcard that matches it -- and reports permissions. It also takes into
account extra permissions enabled by the `setperms` command (see
doc/wildcard-repositories.mkd). It shows you the "creator" of the repo as
an additional column, defaulting to `<gitolite>` if it was not a wildcard
repo.
The limit of number of repos shown in big-config mode (20, by default)
described earlier applies to the "expand" command also.

View file

@ -1,88 +0,0 @@
## avoiding the shell on the server
Gitolite now tries to prevent gitolite-admin push privileges from being used
to obtain a shell on the server. This was not always the case (older gitolite
did not make this distinction), but I've been moving towards this for a while
now, and, while there could still be holes in that separation, they will be
fixed as and when found.
Thus, settings that have security implications can be set only from the rc
file, which needs to be edited directly on the server. And adding a new hook
requires adding it to the *gitolite* clone and running easy install again, or
gl-setup, if you used the server-side install method, both of which require
shell access.
While this is great for my main target (corporate environments), some people
don't like it. They want to do all of this from the *gitolite-admin* repo,
because the security concern mentioned above does not bother them. They don't
want to log on to the server to make a change in the rc file or don't want to
run easy install to propagate a new set of hooks. In addition, they may want
all of these animals versioned in the "gitolite-admin" repo itself, which
certainly makes sense.
So here's how you might do that.
First, arrange to have all your special files added to the gitolite-admin
repo. The best option is to keep all of this in a single subdirectory (let's
call it "local" in our example). So your `~/.gitolite.rc` might go into
`local/gitolite.rc`, and all your local hooks into `local/hooks` etc. Add
them, commit, and push.
Note: do not create any top level directory called "conf", "contrib", "doc",
"hooks", or "src" -- those names are used by gitolite itself.
Second, create a `post-update.secondary` hook and place it in the *gitolite*
clone's `hooks/common` directory, containing the following code:
#!/bin/bash
[ "$GL_REPO" = "gitolite-admin" ] || exit 0
[ -z "$GL_RC" ] && { echo "ENV GL_RC not set"; exit 1; }
GL_ADMINDIR=`$GL_BINDIR/gl-query-rc GL_ADMINDIR`
cp $GL_ADMINDIR/local/gitolite.rc $HOME/.gitolite.rc
cp -a $GL_ADMINDIR/local/hooks/* $GL_ADMINDIR/hooks/common
/Full/Path/To/gl-install -q
# the path should be the same as that for gl-auth-command in the
# "command=" parameter of ~/.ssh/authorized_keys on the server
Don't forget to make it executable!
After this, run the upgrade instructions for the install method you used (just
as if the `post-update.secondary` file you just created came from a gitolite
software update).
All future changes to the rc file can be done via local/gitolite.rc in the
admin repo, and hooks can be added to local/hooks.
**Note**: One quirk of how gitolite [propagates hooks][hpd] is that now this
`post-update.secondary` exists in all normal repos also. Just ignore it; it's
not doing any harm.
[hpd]: http://sitaramc.github.com/gitolite/doc/hook-propagation.html
**Warning**: Nothing in gitolite *removes* hooks, so if you delete (or even
rename) a script, it still stays on the server -- you'll have to delete them
manually from the server.
----
So what's this actually doing?
Well, first, note that `$GL_ADMINDIR` contains files from both gitolite
itself, as well as from the gitolite-admin repo. "conf/VERSION", "src",
"doc", and "hooks" come from gitolite itself, while the other 2 files in
"conf", and all of "keydir" come from the gitolite-admin repo. ("logs"
doesn't come from anywhere).
In addition, any other files in the "master" branch of the gitolite-admin repo
get checked out here, which in this case would mean the entire "local/"
hierarchy you created above.
Now, since the "hooks/common" directory is coming from gitolite itself,
clearly this is where the internal "install" routine expects to find new or
updated hooks to propagate. So you just copy your local hooks (in the
"local/hooks" directory) to "hooks/common" and run the installer again. Done!

View file

@ -1,497 +0,0 @@
# ssh troubleshooting
In this document:
* <a href="#_common_ssh_asks_for_a_password">(common) ssh asks for a password</a>
* <a href="#_problems_when_using_package_root_or_non_root_install_methods">problems when using package, root, or non-root install methods</a>
* <a href="#_problems_when_using_the_from_client_install_method">problems when using the "from-client" install method</a>
* <a href="#_sidebar_why_two_keys_on_client_for_the_admin">(sidebar) why two keys on client for the admin</a>
* <a href="#_bypassing_gitolite_without_intending_to">bypassing gitolite without intending to</a>
* <a href="#_basic_ssh_troubleshooting_for_the_admin">basic ssh troubleshooting for the admin</a>
* <a href="#_problems_with_using_non_openssh_public_keys">problems with using non-openssh public keys</a>
* <a href="#_windows_issues">windows issues</a>
* <a href="#_details">details</a>
* <a href="#_files_on_the_server">files on the server</a>
* <a href="#_files_on_client">files on client</a>
* <a href="#_some_other_tips_and_tricks">some other tips and tricks</a>
* <a href="#_giving_shell_access_to_gitolite_users">giving shell access to gitolite users</a>
* <a href="#_losing_your_admin_key">losing your admin key</a>
* <a href="#_simulating_ssh_copy_id">simulating ssh-copy-id</a>
----
----
This document should help you troubleshoot ssh-related problems in installing
and accessing gitolite.
**This is about 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.**
**I have spent more than my share of time helping people debug their
misconfigured servers, simply because they tried to blame gitolite for their
troubles. This stops now. I'd rather spend time on actual gitolite features,
code, and documentation.**
Other resources:
* people who think this is too hard should take a look at this
[transcript][] to **see how simple it *actually* is**. This transcript
uses the 'from-client' method of install.
* someone also wrote a tutorial, see [here][tut]. This uses the 'non-root'
method of install, to provide a different perspective.
* I **strongly** recommend reading [doc/gitolite-and-ssh.mkd][doc9gas],
which is a very detailed look at how gitolite uses ssh's features on the
server side. Most people don't know ssh as well as they *think* they do;
even if you don't have any problems right now, it's worth skimming over.
* there's a program called `sshkeys-lint` that you can run on your client to
figure out which key is doing what. Run it without arguments to get help
on how to run it and what inputs it needs.
----
<a name="_common_ssh_asks_for_a_password"></a>
### (common) ssh asks for a password
**NOTE**: This section should be useful to anyone trying to get password-less
access working. It is **not** specific to gitolite.
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 `gl-setup` or `gl-easy-install` steps 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.
* make sure the right private key is being offered. Run ssh in very
verbose mode and look for the word "Offering", like so:
ssh -vvvv 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; see next bullet.
If you don't see any offers being made at all, then you probably don't
have any protocol 2 keys in your `~/.ssh` (names are `id_rsa` or `id_dsa`,
with corresponding `.pub` files).
* 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/mykey` and try
the access again.
* 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.
* 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.
----
<a name="_problems_when_using_package_root_or_non_root_install_methods"></a>
### problems when using package, root, or non-root install methods
This section applies if you installed using any of the [first 3 methods][o3]
of install.
In these 3 modes, installation is done on the server, by logging in as some
other user and doing and `su - git`. The admin's workstation has only one key
that is known to the server's authkeys file, and this key invokes gitolite.
**Note** that this key is not supposed to get you a shell; it is supposed to
invoke gitolite.
As a result, it's a lot easier to debug. Just run `ssh git@server info`. If
this get you the [gitolite version and access info][repout], everything is
fine. If it asks you for a password, see the very first section of this
document for help.
If it gets you the GNU info command output, you have shell access. This
probably means you had passwordless shell access to the server *before* you
were added as a gitolite user, and you sent that same key to your gitolite
admin to include in the admin repo. This won't work -- 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.
You'll have to (create and) use a different keypair for gitolite access.
<a name="_problems_when_using_the_from_client_install_method"></a>
### problems when using the "from-client" install method
This section applies if you installed using the [from-client method][fc].
This method of install is unique in that the admin will have 2 distinct keys
to access the server. The default key (`~/.ssh/id_rsa`) is used to get a
shell prompt and to run commands; for example, `gl-easy-install` uses this key
to do all its server-side work.
In addition, there is a named key created just to invoke gitolite instead of
starting a shell. The name is whatever you gave as the third argument to the
`gl-easy-install` command (for example, `~/.ssh/sitaram.pub` in my case).
Finally, a `host gitolite` para is added to `~/.ssh/config`:
host gitolite
user git
hostname server
identityfile ~/.ssh/sitaram
so that you can use `gitolite:reponame` as the URL to make ssh use the named
key.
All this applies *only* to the admin. Normal users will only have one key and
do not need any of this.
<a name="twokeys"></a>
<a name="_sidebar_why_two_keys_on_client_for_the_admin"></a>
#### (sidebar) why two keys on client for the admin
> There are two types of access the admin will make to the server: a normal
> login, to get a shell prompt, and gitolite access (clone/fetch/push etc).
> The first access needs an authkeys line *without* any "command="
> restrictions, while the second requires a line *with* such a restriction.
> And we can't use the same key for both because there is no way to
> disambiguate them; the ssh server will always (*always*) pick the first
> one in sequence when the key is offered by the ssh client.
> So the next question is usually "I have other ways to get a shell on that
> account (like `su - git` from some other account), so why do I need a key
> for shell access at all?"
> The answer to this is that the "easy install" script, being written for
> the most general case, needs shell access via ssh to do its stuff. If you
> have access otherwise, you really should use one of the other 3 install
> methods to install gitolite. Please see the [install doc][install] for
> details.
<a name="_bypassing_gitolite_without_intending_to"></a>
#### bypassing gitolite without intending to
These problems happen to the person who has **utterly failed** to read/heed
the message that shows up at the end of running the `gl-easy-install` command.
Both these problems are caused by using the wrong key, thus **bypassing
gitolite completely**:
* you get `fatal: 'reponame' does not appear to be a git repository`, and
yet you are sure 'reponame' exists, you haven't mis-spelled it, etc.
* you are able to clone repositories but are unable to push changes back
(the error complains about the `GL_RC` environment variable not being set,
and the `hooks/update` failing in some way).
Let us recap the message that appears on a successful run of the "easy-install"
program; it looks something like this (with suitable values substituted for
`<user>`, `<server>`, and `<port>`):
IMPORTANT NOTE -- PLEASE READ!!!
*Your* URL for cloning any repo on this server will be
gitolite:reponame.git
*Other* users you set up will have to use
<user>@<server>:reponame.git
However, if your server uses a non-standard ssh port, they should use
ssh://<user>@<server>:<port>/reponame.git
If this is your first time installing gitolite, please also:
tail -31 src/gl-easy-install
for next steps.
The first error above happens if you use `git@server:reponame` instead of
`gitolite:reponame`. All your repos are actually in a subdirectory pointed to
by `$REPO_BASE` in the rc file (default: `repositories`). Gitolite internally
prefixes this before calling the actual git command you invoked, but since
you're bypassing gitolite completely, this prefixing does not happen, and so
the repo is not found.
The second error happens if you use `git@server:repositories/reponame.git`
(assuming default `$REPO_BASE` setting) -- that is, you used the full unix
path. Since the "prefixing" mentioned above is not required, the shell finds
the repo and clones ok. But 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.
<a name="_basic_ssh_troubleshooting_for_the_admin"></a>
#### basic ssh troubleshooting for the admin
Otherwise, run these checks:
1. `ssh git@server` should get you a command line *without* asking for a
password.
If it asks you for a password, then your `id_rsa` keypair changed after
you ran the easy install, or someone fiddled with the
`~/.ssh/authorized_keys` file on the server.
If it prints the gitolite version and access info (see
[doc/report-output.mkd][repout]), you managed to overwrite the `id_rsa`
keypair with the `sitaram` keypair, or something equally weird. This is
because a gitolite key, when used without any actual command, defaults to
running gitolite's internal "info" command.
**NOTE** starting with [version 5.6][openssh56], openssh will "Kill
channel when pty allocation requests fail". This means that, instead of
seeing the version and access info as described above, you may only get a
message about pty allocation failure, followed by "connection closed".
2. `ssh gitolite info` should print some gitolite version and access info.
If you get the output of the GNU info command instead, you probably reused
your `id_rsa` keypair as your `sitaram` keypair, or overwrote the
`sitaram` keypair with the `id_rsa` keypair.
There are many ways to fix this, depending on where and what the damage is.
The most generic way (and therefore time-taking) is to re-install gitolite
from scratch:
* make a backup of your gitolite-admin repo clone somewhere (basically your
`keydir/*.pub` and your `conf/gitolite.conf`). If necessary get these
files from the server's `~/.gitolite` directory.
* log on to the server somehow (using some other account, using a password,
su-ing in, etc) and delete `~/.ssh/authorized_keys`. Rename or move aside
`~/.gitolite` so that also looks like it is missing.
* back on your workstation, make sure you have 2 keypairs (`id_rsa` and
`sitaram`, along with corresponding `.pub` files). Create them if needed.
Also make sure they are *different* and not a copy of each other :-)
* install gitolite normally:
* run `ssh-copy-id -i ~/.ssh/id_rsa.pub git@server` to get passwordless
access to the server. (Mac users may have to do this step manually)
* make sure `ssh git@server pwd` prints the `$HOME` of `git@server`
**without** asking for a password. Do not proceed till this works.
* run easy install again, (in my case: `cd gitolite-source;
src/gl-easy-install -q git server sitaram`)
* go to your gitolite-admin repo clone, and copy `conf/gitolite.conf` and
`keydir/*.pub` from your backup to this directory
* copy (be sure to overwrite!) `~/.ssh/sitaram.pub` also to keydir
* now `git add keydir; git commit; git push -f`
That's a long sequence but it should work.
<a name="_problems_with_using_non_openssh_public_keys"></a>
### 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) results in:
WARNING: a pubkey file can only have one line (key); ignoring YourName.pub
To convert ssh2-compatible keys to openssh run:
ssh-keygen -i -f /tmp/ssh2/YourName.pub > /tmp/openssh/YourName.pub
then use the resulting pubkey as you normally would in gitolite.
<a name="_windows_issues"></a>
### 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.
If you can offer an *authoritative* account of the complications involved, and
how to resolve them and get things working, I'd be happy to credit you and
include it, either directly here if it is short enough or just an external
link, or in contrib/ if it's a longer piece of text.
<a name="_details"></a>
### details
Here's how it all hangs together.
<a name="_files_on_the_server"></a>
#### files on the server
* the authkeys file; this contains one line containing the pubkey of each
user who is permitted to login without a password.
Pubkey lines that give shell access look like this:
ssh-rsa AAAAB3NzaC[snip]uPjrUiAUew== /home/sitaram/.ssh/id_rsa
On a typical server there will be only one or two of these lines.
Note that the last bit (`/home/sitaram/.ssh/id_rsa`) is purely a *comment*
field and can be anything. Also, the actual lines are much longer, about
400 characters; I snipped 'em in the middle, as you can see.
In contrast, pubkey lines that give access to git repos hosted by gitolite
look like this:
command="[some path]src/gl-auth-command sitaram",[some restrictions] ssh-rsa AAAAB3NzaC[snip]s18OnB42oQ== sitaram@sita-lt
You will have many more of these lines -- one for every pubkey file in
`keydir/` of your gitolite-admin repo, with the corresponding username in
place of "sitaram" in the example above.
The "command=" at the beginning ensures that when someone with the
corresponding private key logs in, they don't get a shell. Instead, the
`gl-auth-command` program is run, and (in this example) is given the
argument `sitaram`. This is how gitolite is invoked, (and is told the
user logging in is "sitaram").
<a name="_files_on_client"></a>
#### files on client
* default keypair; used to get shell access to servers. You would have
copied this pubkey to the gitolite server in order to log in without a
password. (On Linux systems you may have used `ssh-copy-id` to do that).
You would have done this *before* you ran the easy install script, because
otherwise easy install won't run!
~/.ssh/id_rsa
~/.ssh/id_rsa.pub
* gitolite keypair; the "sitaram" in this is the 3rd argument to the
`src/gl-easy-install` command you ran; the easy install script does the
rest
~/.ssh/sitaram
~/.ssh/sitaram.pub
* config file; this file has an entry for gitolite access if you install
usine the "from-client" method. (See above for example "host gitolite"
para in the ssh config file).
This is needed because this is the only way to force git to use a
non-default keypair (unlike command line ssh, which can be given the `-i
~/.ssh/sitaram` flag to do so).
<a name="_some_other_tips_and_tricks"></a>
### some other tips and tricks
<a name="_giving_shell_access_to_gitolite_users"></a>
#### giving shell access to gitolite users
We've managed (thanks to an idea from Jesse Keating) to make it possible for a
single key to allow both gitolite access *and* shell access.
This is done by copying the pubkey (to which you want to give shell access) to
the server and running either
cd $HOME/.gitolite # assuming default $GL_ADMINDIR in ~/.gitolite.rc
src/gl-tool shell-add ~/foo.pub
or
gl-tool shell-add ~/foo.pub
The first method is applicable if you installed using the **from-client**
method, while the second method is for any of the other three (see
doc/1-INSTALL.mkd, section on "install methods", for more on this)
**IMPORTANT UPGRADE NOTE**: previous implementations of this feature were
crap. There was no easy/elegant way to ensure that someone who had repo admin
access would not manage to get himself shell access.
Giving someone shell access requires that you should have shell access in the
first place, so the simplest way is to enable it from the server side only.
<a name="_losing_your_admin_key"></a>
#### losing your admin key
If you lost the admin key, and need to re-establish ownership of the
gitolite-admin repository with a fresh key, take a look at the
`src/gl-dont-panic` program. You will need shell access to the server of
course. Run it without arguments to get instructions.
<a name="_simulating_ssh_copy_id"></a>
#### 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!)]
[doc9gas]: http://sitaramc.github.com/gitolite/doc/gitolite-and-ssh.html
[install]: http://sitaramc.github.com/gitolite/doc/1-INSTALL.html
[o3]: http://sitaramc.github.com/gitolite/doc/1-INSTALL.html#methods
[fc]: http://sitaramc.github.com/gitolite/doc/1-INSTALL.html#fc
[urls]: http://sitaramc.github.com/gitolite/doc/1-INSTALL.html#URLs_for_gitolite_managed_repos
[repout]: http://sitaramc.github.com/gitolite/doc/report-output.html
[transcript]: http://sitaramc.github.com/gitolite/doc/install-transcript.html
[openssh56]: http://www.openssh.org/txt/release-5.6
[tut]: http://sites.google.com/site/senawario/home/gitolite-tutorial

View file

@ -1,67 +0,0 @@
## uninstalling gitolite
Sometimes you might find gitolite is overkill -- you have only one user
(yourself) pushing maybe. Or maybe gitolite is just not enough -- you want a
web-based front end that users can use to manage their keys themselves, etc.,
in which case you'd probably switch to [github][g1], [girocco][g2],
[indefero][g3] or [gitorious][g4]. [Gerrit][g5] is quite nice too, if you
want collaborative code review there's nothing like it. Either way, you'd
like to uninstall gitolite.
[g1]: http://github.com
[g2]: http://repo.or.cz/w/girocco.git
[g3]: http://www.indefero.net/
[g4]: http://gitorious.com/
[g5]: http://code.google.com/p/gerrit/
Uninstalling gitolite is fairly easy, although it is manual. (We'll assume
`$REPO_BASE` in the rc file was left at its default of `~/repositories`; if
not, adjust accordingly):
**server side tasks**
* edit `~/.ssh/authorized_keys` and delete the `# gitolite start` and `#
gitolite end` markers and all the lines between them. This will prevent
any of your users from attempting a push while you are doing this.
If you are the only user, and/or *need* one or more of those keys to
continue to access this account (like if one of them is your laptop or
your home desktop etc.) then instead of deleting the line you can just
delete everything upto but not including the words "ssh-rsa" or "ssh-dss".
* Now remove (or move aside or rename to something else if you're paranoid)
the following files and directories.
~/.gitolite
~/.gitolite.rc
~/repositories/gitolite-admin.git
* You can remove all of `~/repositories` if you have not really started
using gitolite properly yet; that's your choice.
If you *do* need to preserve the other repos and continue to use them,
remove all the `update` hooks that git installs on each repository. The
easiest way is:
find ~/repositories -wholename "*.git/hooks/update" | xargs rm -f
but you can do it manually if you want to be careful.
**client side tasks**
* Any remote users that still have access must update their clone's remote
URLs (edit `.git/config` in the repo) to prefix `repositories/` before the
actual path used, in order for the remote to still work. This is because
you'll now be accessing it through plain ssh, which means you have to give
it the full path.
* Finally, you as the gitolite admin will probably have a host stanza for
"gitolite" in your *client*'s `~/.ssh/config`. Find and delete lines that
look like this:
host gitolite
user git
hostname your.server
port 22
identityfile ~/.ssh/your-gitolite-admin-username

View file

@ -1,59 +0,0 @@
# who uses gitolite
> > If you're using gitolite and find it very useful in some way, I would
> > love to describe your use of it or add a link to your own description
> > of it here. Of course, you can anonymise it as much as you need to.
The **Fedora Project** controls access to over 10,000 package management
repositories accessed by over 1,000 package maintainers [using
gitolite][fedora]. This is probably the largest *confirmed* gitolite
installation anywhere. The whole [big-config][bc] thing was initially done
for them (their config file was so big that without the big-config changes
gitolite would just run out of memory and die!).
[fedora]: http://lists.fedoraproject.org/pipermail/devel-announce/2010-July/000647.html
[bc]: http://sitaramc.github.com/gitolite/doc/big-config.html
The **KDE project** [uses][kde] gitolite (in combination with redmine for
issue tracking and reviewboard for code review). Apart from the usual access
control, the KDE folks are heavy users of the "ad hoc repo creation" features
enabled by wildrepos and the accompanying commands. Several of the changes to
the "admin defined commands" were also inspired by KDE's needs. See [section
5][s5] and [section 6][s6] of the above linked page for details.
[kde]: http://community.kde.org/Sysadmin/GitKdeOrgManual
[s5]: http://community.kde.org/Sysadmin/GitKdeOrgManual#Server-side_commands
[s6]: http://community.kde.org/Sysadmin/GitKdeOrgManual#Personal_repositories
[kdera]: http://permalink.gmane.org/gmane.comp.kde.scm-interest/1437
**Prof. Hiren Patel** of the University of Waterloo is responsible for the
existence of the fairly popular "[wildrepos][wild]" feature. The
documentation was pretty much written with his use case in mind, but of course
it turns out to be useful for a lot of people, as you can see from the
previous para on KDE's use of gitolite.
In fact, he surprised the heck out of me recently by saying that if it hadn't
been for this feature, he might not have used git itself -- which is a pretty
serious compliment if you think about the magnitude of the git project and my
little one-man show!
He explains his use of it [here][hiren].
[wild]: http://sitaramc.github.com/gitolite/doc/wildcard-repositories.html
[hiren]: http://ece.uwaterloo.ca/~hdpatel/uwhtml/?p=470
**Gentoo Linux** has [just moved][gentoo1] their git repositories from gitosis
to gitolite. There are about 200 repositories, some of them are the so called
[overlays][gentoo2], official and unofficial/user overlays, plus several
developer and project repositories, used by more than 1000 people. That number
will be increased in the near future, as they are going to migrate some of
their CVS/SVN repositories there, plus they are offering overlays hosting for
users as well.
[gentoo1]: http://archives.gentoo.org/gentoo-dev/msg_2812c9b9e768f64b46360ab17b9d0024.xml
[gentoo2]: http://www.gentoo.org/proj/en/overlays/
**Nokia MeeGo** uses Gitolite internally, and has also contributed LDAP
specific code (see [contrib/ldap][ldap] directory for details).
[ldap]: http://github.com/sitaramc/gitolite/blob/pu/contrib/ldap

View file

@ -1,346 +0,0 @@
## repositories named with wildcards
***IMPORTANT NOTE***:
This feature may be somewhat "brittle" in terms of security. Creating
repositories based on wild cards, giving "ownership" to the specific user who
created it, allowing him/her to hand out permissions to other users to
collaborate, all these are possible. And any of these could have a bug in it.
I haven't found any yet, but that doesn't mean there aren't any.
----
In this document:
* <a href="#_quick_introduction">quick introduction</a>
* <a href="#_rc_file_setting_required">rc file setting required</a>
* <a href="#_examples_of_wildcard_repos">examples of wildcard repos</a>
* <a href="#_wildcard_repos_with_creator_name_in_them">wildcard repos with creator name in them</a>
* <a href="#_wildcard_repos_without_creator_name_in_them">wildcard repos without creator name in them</a>
* <a href="#_side_note_valid_regexes">side-note: valid regexes</a>
* <a href="#_side_note_line_anchored_regexes">side-note: line-anchored regexes</a>
* <a href="#_contrast_with_refexes">contrast with refexes</a>
* <a href="#_handing_out_rights_to_wildcard_matched_repos">handing out rights to wildcard-matched repos</a>
* <a href="#_admin_adding_other_categories_than_READERS_and_WRITERS">(admin) adding other categories than READERS and WRITERS</a>
* <a href="#_IMPORTANT_WARNING_ABOUT_THIS_FEATURE_">**IMPORTANT WARNING ABOUT THIS FEATURE**</a>
* <a href="#_setting_a_gitweb_description_for_a_wildcard_matched_repo">setting a gitweb description for a wildcard-matched repo</a>
* <a href="#_reporting">reporting</a>
* <a href="#_how_it_actually_works">how it actually works</a>
----
This document is mostly "by example".
----
<a name="_quick_introduction"></a>
### 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. The examples below will make this clearer.
<a name="_rc_file_setting_required"></a>
### rc file setting required
This feature requires that you set `$GL_WILDREPOS` to "1" in `~/.gitolite.rc`
on the server. Please search for that variable in `doc/gitolite.rc.mkd`
for more information on this.
<a name="_examples_of_wildcard_repos"></a>
### examples of wildcard repos
As the introduction said, you can include the word `CREATOR` in the regex
pattern, though it is not mandatory. We'll look at examples of both types of
usage.
Which of these alternatives you choose depends on your needs, and the social
aspects of your environment. Including the creator name in the pattern keeps
users rigidly separated from each others repos, and is good for a largely
autonomous collection of users with a high probability of repo name clashes.
Omitting the creator name from the pattern puts the repos in a common
namespace, and is suitable for environments where it is not very important to
keep track of who actually created the repo (except for granting access), but
needs more communication / co-operation among the users to avoid repo name
clashes.
<a name="_wildcard_repos_with_creator_name_in_them"></a>
#### wildcard repos with creator name in them
Here's an example snippet:
@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
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/t/a12/.git/
Initialized empty Git repository in /home/gitolite/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 name="_wildcard_repos_without_creator_name_in_them"></a>
#### wildcard repos without creator name in them
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.
In either case, they could then use the `setperms` feature to specify which
users are "READERS" and which are "WRITERS". See later for details.
<a name="_side_note_valid_regexes"></a>
### side-note: valid regexes
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].*`.
<a name="_side_note_line_anchored_regexes"></a>
### side-note: 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.
<a name="_contrast_with_refexes"></a>
#### 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.
<a name="_handing_out_rights_to_wildcard_matched_repos"></a>
### handing out rights to wildcard-matched repos
In the examples above, we saw two special "user" names: READERS and WRITERS.
The permissions they have are controlled by the config file, but ***who is
part of this list*** is controlled by the person who created the repository.
The use case is that, although our toy example has only 3 students, in reality
there will be a few dozen, but each assignment will be worked on only by a
handful from among those. This allows the creator to take ad hoc sets of
users from among the actual users in the system, and place them into one of
two categories (in this example, READERS and WRITERS respectively). In theory
you could do the same thing by creating lots of little "assignment-NN" groups
in the config file but that may be a little too cumbersome for non-secret
environments.
Create a small text file that contains the permissions you desire:
$ cat > myperms
READERS u5
WRITERS u6
(hit ctrl-d here)
...and use the new "setperms" command to set permissions for your repo:
$ ssh git@server setperms assignments/u4/a12 < myperms
New perms are:
READERS u5
WRITERS u6
'setperms' will helpfully print what the new permissions are but you can also
use 'getperms' to check:
$ ssh git@server getperms assignments/u4/a12
READERS u5
WRITERS u6
The following points are important:
* note the syntax of the commands; it's not a "git" command, and there's no
`:` like in a repo URL. The first space-separated word is READERS or
WRITERS, and the rest are simple usernames.
<a name="_admin_adding_other_categories_than_READERS_and_WRITERS"></a>
### (admin) adding other categories than READERS and WRITERS
Let's say your needs are more complex and you need more categories of users.
For example, you might like to have a setup where only a tester can update
tags, and only a manager can delete branches:
repo foo/..*
C = u1
RW refs/tags/ = TESTERS
- refs/tags/ = @all
RW+ = WRITERS
RW = INTERNS
R = READERS
RW+D = MANAGERS
As you can see, someone pre-creates the repo and assigns rights to various
people, say by sending something like this to `setperms`:
READERS wally
WRITERS dilbert alice
MANAGERS phb
INTERNS ashok
TESTERS ashok
You can enable this by setting the `GL_WILDREPOS_PERM_CATS` variable in the rc
file. The rc file documentation (`doc/gitolite.rc.mkd`) explains how.
<a name="_IMPORTANT_WARNING_ABOUT_THIS_FEATURE_"></a>
#### **IMPORTANT WARNING ABOUT THIS FEATURE**
Please make sure that none of the category names conflict with any of the
**usernames** in the system. For example, if you have a user called "foo",
make sure you do not include "foo" as a valid category in
`$GL_WILDREPOS_PERM_CATS`.
You can keep things sane by using UPPERCASE names for categories, while
keeping all your usernames lowercase; then you don't have to worry about this
problem.
<a name="_setting_a_gitweb_description_for_a_wildcard_matched_repo"></a>
### setting a gitweb description for a wildcard-matched repo
Similar to the getperms/setperms commands, there are the getdesc/setdesc
commands, thanks to Teemu.
<a name="_reporting"></a>
### reporting
In order to see what repositories were created from a wildcard, use the
"expand" command, described briefly in [doc/report-output.mkd][repout].
### deleting a wild repo
See [repo deletion][rmr] for more on this. Note that this requires you to
install/setup "adc"s (admin defined commands). See
[doc/admin-defined-commands.mkd][adc] for how to do that.
[adc]: http://sitaramc.github.com/gitolite/doc/admin-defined-commands.html
[rmr]: http://github.com/sitaramc/gitolite/blob/pu/contrib/adc/repo-deletion.README
<a name="_how_it_actually_works"></a>
### how it actually works
This section tells you what is happening inside gitolite so you can understand
this feature better. Let's use the config example at the beginning of this
document:
repo assignments/CREATOR/a[0-9][0-9]
C = @students
RW+ = CREATOR
RW = WRITERS @TAs
R = READERS @prof
First we find the set of rules to apply. This involves replacing the special
words CREATOR, WRITERS, and READERS with appropriate usernames to derive an
"effective" ruleset for the repo in question.
For a **new** repo, replace the word CREATOR in all repo patterns and rules
with the name of the invoking user.
> (Note: this is why you should never use `C = CREATOR`; it becomes `C =
> invoking_user`! Unless you really want to allow *all* users to create
> repos, you should restrict "C" perms to an actual user or set of users,
> like in the examples in this document).
For an **existing** repo, do the same but replace with the name of the user
who actually *created* the repo (this name is recorded in a special file in
the repo directory when the repo is first created, so it is available).
Now find a repo pattern that matches the actual reponame being pushed -- this
tells you which set of rules to apply. There can be multiple matches; if so,
they will all be applied in the sequence they appear in the config file.
If the invoking user has been put in the "WRITERS" category using `setperms`, all
permissions for the the user WRITERS are given to the invoking username (and
similarly for READERS).
At this point we have an effective ruleset, and the normal access rules (R,
RW, etc) apply, with the addition that the invoking user needs "C" access to
be able to create a repo.
> (Note: "C" rights do not automatically give the CREATOR any other rights;
> they must be specifically given. `RW+ = CREATOR` is recommended in most
> situations, as you can see in our example).
Assuming user "u4" trying to push-create a new repo called
`assignments/u4/a23`, this is what the effective ruleset looks like:
repo assignments/u4/a23
C = @students
RW+ = u4
RW = @TAs
R = @prof
If u4 puts u5 in the "WRITERS" category using `setperms`, and u5 tries to
access that repo, the ruleset looks like:
repo assignments/u4/a23
C = @students
RW+ = u4
RW = u5 @TAs
R = @prof
I hope that helps.
----
Enjoy, and please use with care. This is pretty powerful stuff. As they say:
if you break it, you get to keep both pieces :)
[repout]: http://sitaramc.github.com/gitolite/doc/report-output.html

View file

@ -1,10 +0,0 @@
#!/bin/bash
# sample pre-git hook to print pending hub requests
[ "$1" = "R" ] && exit 0 # we only want to print them on pushes
# print 'fetched' and 'pending' requests only
SSH_ORIGINAL_COMMAND="hub list-requests $GL_REPO fetched pending" $GL_BINDIR/gl-auth-command $GL_USER
exit 0

View file

@ -1,26 +0,0 @@
#!/bin/bash
# gitolite mirroring
# please see doc/mirroring.mkd for instructions on how to use this
# flush STDIN coming from git; we have no use for that info in this hook but
# if you don't do this, git-shell sometimes dies of a signal 13 (SIGPIPE)
[ -t 0 ] || cat >/dev/null
if [ -n "$GL_SLAVES" ]
then
for mirror in $GL_SLAVES
do
if git push --mirror $mirror:$GL_REPO.git
then
:
else
ssh $mirror mkdir -p $GL_REPO.git
ssh $mirror git init --bare $GL_REPO.git
ssh $mirror "cd $GL_REPO.git; git config receive.fsckObjects true"
git push --mirror $mirror:$GL_REPO.git ||
echo "WARNING: mirror push to $mirror failed"
fi
done
fi >&2

View file

@ -1,99 +0,0 @@
#!/usr/bin/perl
use strict;
use warnings;
# === update ===
# this is gitolite's update hook
# ----------------------------------------------------------------------------
# find the rc file, then pull the libraries
# ----------------------------------------------------------------------------
BEGIN {
# people with shell access should be allowed to bypass the update hook,
# simply by setting an env var that the ssh "front door" will never set
exit 0 if exists $ENV{GL_BYPASS_UPDATE_HOOK};
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
}
use lib $ENV{GL_BINDIR};
use gitolite_rc;
use gitolite qw(:DEFAULT %repos);
# ----------------------------------------------------------------------------
# start...
# ----------------------------------------------------------------------------
my ($perm, $creator, $wild) = repo_rights($ENV{GL_REPO});
my $reported_repo = $ENV{GL_REPO} . ( $wild ? " ($wild)" : "" );
# arguments are as supplied to an update hook by git; man githooks
my ($ref, $oldsha, $newsha) = @ARGV;
my $merge_base = '0' x 40;
# compute a merge-base if both SHAs are non-0, else leave it as '0'x40
# (i.e., for branch create or delete, merge_base == '0'x40)
chomp($merge_base = `git merge-base $oldsha $newsha`)
unless $oldsha eq '0' x 40
or $newsha eq '0' x 40;
# att_acc == attempted access -- what are you trying to do? (is it 'W' or '+'?)
my $att_acc = 'W';
# rewriting a tag is considered a rewind, in terms of permissions
$att_acc = '+' if $ref =~ m(refs/tags/) and $oldsha ne ('0' x 40);
# non-ff push to ref
# notice that ref delete looks like a rewind, as it should
$att_acc = '+' if $oldsha ne $merge_base;
# were any 'D' perms specified? If they were, it means we have to separate
# deletes from rewinds, so if the new sha is all 0's, change the '+' to a 'D'
$att_acc = 'D' if ( $repos{$ENV{GL_REPO}}{DELETE_IS_D} or $repos{'@all'}{DELETE_IS_D} ) and $newsha eq '0' x 40;
# similarly C for create a branch
$att_acc = 'C' if ( $repos{$ENV{GL_REPO}}{CREATE_IS_C} or $repos{'@all'}{CREATE_IS_C} ) and $oldsha eq '0' x 40;
my @allowed_refs;
# @all repos: see comments in similar code in check_access
push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$ENV{GL_USER}} || [] };
push @allowed_refs, @ { $repos{'@all'} {$ENV{GL_USER}} || [] };
push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{'@all'} || [] };
# prepare the list of refs to be checked
# previously, we just checked $ref -- the ref being updated, which is passed
# to us by git (see man githooks). Now we also have to treat each NAME being
# updated as a potential "ref" and check that, if NAME-based restrictions have
# been specified
my @refs = ($ref); # the first ref to check is the real one
# because making it work screws up efficiency like no tomorrow...
if (exists $repos{$ENV{GL_REPO}}{NAME_LIMITS}) {
# this is special to git -- the hash of an empty tree
my $empty='4b825dc642cb6eb9a060e54bf8d69288fbee4904';
# well they're not really "trees" but $empty is indeed the empty tree so
# we can just pretend $oldsha/$newsha are also trees, and anyway 'git
# diff' only wants trees
my $oldtree = $oldsha eq '0' x 40 ? $empty : $oldsha;
my $newtree = $newsha eq '0' x 40 ? $empty : $newsha;
push @refs, map { chomp; s/^/NAME\//; $_; } `git diff --name-only $oldtree $newtree`;
}
# we potentially have many "refs" to check. The one we print in the log is
# the *first* one (which is a *real* ref, like refs/heads/master), while all
# the rest (if they exist) are like NAME/something. So we do the first one
# separately to capture it, then run the rest (if any)
my $log_refex = check_ref(\@allowed_refs, $ENV{GL_REPO}, (shift @refs), $att_acc);
check_ref (\@allowed_refs, $ENV{GL_REPO}, $_ , $att_acc) for @refs;
# if we returned at all, all the checks succeeded. Check secondary hooks now
$UPDATE_CHAINS_TO ||= 'hooks/update.secondary';
( -f $UPDATE_CHAINS_TO or -l $UPDATE_CHAINS_TO )
and system ( $UPDATE_CHAINS_TO, @ARGV )
and die "$UPDATE_CHAINS_TO died\n";
# now log it and exit 0 so git can get on with it
log_it("", "$att_acc\t" . substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14) .
"\t$reported_repo\t$ref\t$log_refex");
exit 0;

View file

@ -1,44 +0,0 @@
#!/bin/bash
# driver script to run multiple update "hooklets". Each "hooklet" performs a
# specific (possibly site-local) check, and they *all* have to succeed for the
# push to succeed.
# HOW TO USE:
# (1) clone gitolite as you would for an upgrade (or install)
# (2) rename this file to remove the .sample extension
# (3) make the renamed file executable (chmod +x)
# (4) create a directory called update.secondary.d in hooks/common
# (5) copy all the update "hooklets" you want (like update.detect-dup-pubkeys)
# from contrib or wherever, to that directory
# (6) make them also executable (chmod +x)
# (7) install/upgrade gitolite as normal so all these files go to the server
# rules for writing a hooklet are in the sample hooklet called
# "update.detect-dup-pubkeys" in contrib
# ----
# NOTE: a hooklet runs under the same assumptions as the 'update' hook, so the
# starting directory must be maintained and arguments must be passed on.
[ -d hooks/update.secondary.d ] || exit 0
# all output from these "hooklets" must go to STDERR to avoid confusing the client
exec >&2
for i in hooks/update.secondary.d/*
do
[ -x "$i" ] || continue
# call the hooklet with the same arguments we got
"$i" "$@" || {
# hooklet failed; we need to log it...
echo hooklet $i failed
perl -I$GL_BINDIR -Mgitolite -e "log_it('hooklet $i failed')"
# ...and send back some non-zero exit code ;-)
exit 1
}
done
exit 0

View file

@ -1,32 +0,0 @@
#!/bin/sh
[ -z "$GL_RC" ] && die "ENV GL_RC not set"
[ -z "$GL_BINDIR" ] && die "ENV GL_BINDIR not set"
[ -z "$GL_ADMINDIR" ] && die "ENV GL_ADMINDIR not set"
# ensure that the admin is not sneaking in src/ and hooks/ :)
GIT_WORK_TREE=$GL_ADMINDIR git ls-tree --name-only master |
perl -lne 'exit 1 if /^(src|hooks)$/' || {
echo "*** ERROR ***" >&2
echo "no files/dirs called 'src' or 'hooks' are allowed, sorry" >&2
echo "until those files are deleted, the post-update hook will not run" >&2
exit 1
}
# checkout the master branch to $GL_ADMINDIR
# (the GL_ADMINDIR env var would have been set by gl-auth-command)
GIT_WORK_TREE=$GL_ADMINDIR git checkout -f --quiet master
od=$PWD
cd $GL_ADMINDIR
$GL_BINDIR/gl-compile-conf
cd $od
ADMIN_POST_UPDATE_CHAINS_TO=`$GL_BINDIR/gl-query-rc ADMIN_POST_UPDATE_CHAINS_TO`
[ -n "$ADMIN_POST_UPDATE_CHAINS_TO" ] || ADMIN_POST_UPDATE_CHAINS_TO=hooks/post-update.secondary
if [ -f $ADMIN_POST_UPDATE_CHAINS_TO ] || [ -L $ADMIN_POST_UPDATE_CHAINS_TO ]
then
exec $ADMIN_POST_UPDATE_CHAINS_TO "$@"
fi

77
install Executable file
View file

@ -0,0 +1,77 @@
#!/usr/bin/perl
use strict;
use warnings;
# Clearly you don't need a program to make one measly symlink, but the git
# describe command involved in generating the VERSION string is a bit fiddly.
use Getopt::Long;
use FindBin;
# meant to be run from the root of the gitolite tree, one level above 'src'
BEGIN { $ENV{GL_BINDIR} = $FindBin::RealBin . "/src"; }
BEGIN { $ENV{GL_LIBDIR} = "$ENV{GL_BINDIR}/lib"; }
use lib $ENV{GL_LIBDIR};
use Gitolite::Common;
=for usage
Usage (from gitolite clone directory):
./install
to run gitolite using an absolute or relative path, for example
'src/gitolite' or '/full/path/to/this/dir/src/gitolite'
./install -ln [<dir>]
to symlink just the gitolite executable to some <dir> that is in
$PATH. <dir> defaults to $HOME/bin if <dir> not specified. <dir> is
assumed to exist; gitolite will not create it.
Please provide a full path, not a relative path.
./install -to <dir>
to copy the entire 'src' directory to <dir>. If <dir> is not in
$PATH, use the full path to run gitolite commands.
Please provide a full path, not a relative path.
Simplest use, if $HOME/bin exists and is in $PATH, is:
git clone git://github.com/sitaramc/gitolite
gitolite/install -ln
# now run setup
gitolite setup -pk /path/to/YourName.pub
=cut
my ( $to, $ln, $help, $quiet );
GetOptions(
'to=s' => \$to,
'ln:s' => \$ln,
'help|h' => \$help,
'quiet|q' => \$quiet,
);
usage() if $to and $ln or $help;
$ln = "$ENV{HOME}/bin" if defined($ln) and not $ln;
for my $d ($ln, $to) {
if ($d and not -d $d) {
print STDERR "FATAL: '$d' does not exist.\n";
usage();
}
}
chdir($ENV{GL_BINDIR});
my $version = `git describe --tags --long --dirty=-dt`;
if ($to) {
_mkdir($to);
system("cp -a * $to");
_print( "$to/VERSION", $version );
} elsif ($ln) {
ln_sf( $ENV{GL_BINDIR}, "gitolite", $ln );
_print( "VERSION", $version );
} else {
say "use the following full path for gitolite:";
say "\t$ENV{GL_BINDIR}/gitolite";
_print( "VERSION", $version );
}

51
src/VREF/COUNT Executable file
View file

@ -0,0 +1,51 @@
#!/bin/sh
# gitolite VREF to count number of changed/new files in a push
# see gitolite docs for what the first 7 arguments mean
# inputs:
# arg-8 is a number
# arg-9 is optional, and can be "NEWFILES"
# outputs (STDOUT)
# arg-7 if the number of changed (or new, if arg-9 supplied) files is > arg-8
# otherwise nothing
# exit status:
# always 0
die() { echo "$@" >&2; exit 1; }
[ -z "$8" ] && die "not meant to be run manually"
newsha=$3
oldtree=$4
newtree=$5
refex=$7
max=$8
nf=
[ "$9" = "NEWFILES" ] && nf='--diff-filter=A'
# NO_SIGNOFF implies NEWFILES
[ "$9" = "NO_SIGNOFF" ] && nf='--diff-filter=A'
# 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 -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" ] && {
author_email=$(git log --format=%ae -1 $newsha)
git cat-file -p $newsha |
egrep -i >/dev/null "^ *$count +new +files +signed-off by: *$author_email *$" && exit 0
echo $refex top commit message should include the text \'$count new files signed-off by: $author_email\'
exit 0
}
echo -n $refex "(too many "
[ -n "$nf" ] && echo -n "new " || echo -n "changed "
echo "files in this push)"
}
exit 0

66
src/VREF/EMAIL-CHECK Executable file
View file

@ -0,0 +1,66 @@
#!/usr/bin/perl
# gitolite VREF to check if all *new* commits have author == pusher
# THIS IS NOT READY TO USE AS IS
# ------------------------------
# you MUST change the 'email_ok()' sub to suit *YOUR* site's
# gitolite username -> author email mapping!
# See bottom of the program for important philosophical notes.
use strict;
use warnings;
# mapping between gitolite userid and correct email address is encapsulated in
# this subroutine; change as you like
sub email_ok {
my ($author_email) = shift;
my $expected_email = "$ENV{GL_USER}\@atc.tcs.com";
return $author_email eq $expected_email;
}
my ( $ref, $old, $new ) = @ARGV;
for my $rev (`git log --format="%ae\t%h\t%s" $new --not --all`) {
chomp($rev);
my ( $author_email, $hash, $subject ) = split /\t/, $rev;
# again, we use the trick that a vref can just choose to die instead of
# passing back a vref, having it checked, etc., if it's more convenient
die "$ENV{GL_USER}, you can't push $hash authored by $author_email\n" . "\t(subject of commit was $subject)\n"
unless email_ok($author_email);
}
exit 0;
__END__
The following discussion is for people who want to enforce this check on ALL
their developers (i.e., not just the newbies).
Doing this breaks the "D" in "DVCS", forcing all your developers to work to a
centralised model as far as pushes are concerned. It prevents amending
someone else's commit and pushing (this includes rebasing, cherry-picking, and
so on, which are all impossible now). It also makes *any* off-line
collabaration between two developers useless, because neither of them can push
the result to the server.
PHBs should note that validating the committer ID is NOT the same as reviewing
the code and running QA/tests on it. If you're not reviewing/QA-ing the code,
it's probably worthless anyway. Conversely, if you *are* going to review the
code and run QA/tests anyway, then you don't really need to validate the
author email!
In a DVCS, if you *pushed* a series of commits, you have -- in some sense --
signed off on them. The most formal way to "sign" a series is to tack on and
push a gpg-signed tag, although most people don't go that far. Gitolite's log
files are designed to preserve that accountability to *some* extent, though;
see contrib/adc/who-pushed for an admin defined command that quickly and
easily tells you who *pushed* a particular commit.
Anyway, the point is that the only purpose of this script is to
* pander to someone who still has not grokked *D*VCS
OR
* tick off an item in some stupid PHB's checklist

45
src/VREF/FILETYPE Executable file
View file

@ -0,0 +1,45 @@
#!/bin/sh
# gitolite VREF to find autogenerated files
# *completely* site specific; use it as an illustration of what can be done
# with gitolite VREFs if you wish
# see gitolite docs for what the first 7 arguments mean
# inputs:
# arg-8 is currently only one possible value: AUTOGENERATED
# outputs (STDOUT)
# arg-7 if any files changed in the push look like they were autogenerated
# otherwise nothing
# exit status:
# always 0
die() { echo "$@" >&2; exit 1; }
[ -z "$8" ] && die "not meant to be run manually"
newsha=$3
oldtree=$4
newtree=$5
refex=$7
option=$8
[ "$option" = "AUTOGENERATED" ] && {
# currently we only look for ".java" programs with the string "Generated
# by the protocol buffer compiler. DO NOT EDIT" in them.
git log --name-only $nf --format=%n $newtree --not --all |
grep . |
sort -u |
grep '\.java$' |
while read fn
do
git show "$newtree:$fn" | egrep >/dev/null \
'Generated by the protocol buffer compiler. +DO NOT EDIT' ||
continue
echo $refex
exit 0
done
}

40
src/VREF/MAX_NEWBIN_SIZE Executable file
View file

@ -0,0 +1,40 @@
#!/usr/bin/perl
use strict;
use warnings;
# gitolite VREF to check max size of new binary files
# see gitolite docs for what the first 7 arguments mean
# inputs:
# arg-8 is a number
# outputs (STDOUT)
# arg-7 if any new binary files exist that are greater in size than arg-8
# *and* there is no "signed-off by" line for such a file in the top commit
# message.
#
# Otherwise nothing
# exit status:
# always 0
die "not meant to be run manually" unless $ARGV[7];
my ( $newsha, $oldtree, $newtree, $refex, $max ) = @ARGV[ 2, 3, 4, 6, 7 ];
# / (.*) +\| Bin 0 -> (\d+) bytes/
chomp( my $author_email = `git log --format=%ae -1 $newsha` );
my $msg = `git cat-file -p $newsha`;
$msg =~ s/\t/ /g; # makes our regexes simpler
for my $newbin (`git diff --stat=999,999 $oldtree $newtree | grep Bin.0.-`) {
next unless $newbin =~ /^ (.*) +\| +Bin 0 -> (\d+) bytes/;
my ( $f, $s ) = ( $1, $2 );
next if $s <= $max;
next if $msg =~ /^ *$f +signed-off by: *$author_email *$/mi;
print "$refex $f is larger than $max";
}
exit 0

49
src/VREF/MERGE-CHECK Normal file
View file

@ -0,0 +1,49 @@
#!/usr/bin/perl
use strict;
use warnings;
# gitolite VREF to check if there are any merge commits in the current push.
# THIS IS DEMO CODE; please read all comments below as well as
# doc/vref.mkd before trying to use this.
# usage in conf/gitolite.conf goes like this:
# - VREF/MERGE_CHECK/master = @all
# # reject only if the merge commit is being pushed to the master branch
# - VREF/MERGE_CHECK = @all
# # reject merge commits to any branch
my $ref = $ARGV[0];
my $oldsha = $ARGV[1];
my $newsha = $ARGV[2];
my $refex = $ARGV[6];
# The following code duplicates some code from parse_conf_line() and some from
# check_ref(). This duplication is the only thing that is preventing me from
# removing the "M" permission code from 'core' gitolite and using this
# instead. However, it does demonstrate how you would do this if you had to
# create any other similar features, for example someone wanted "no non-merge
# first-parent", which is far too specific for me to add to 'core'.
# -- begin duplication --
my $branch_refex = $ARGV[7] || '';
if ($branch_refex) {
$branch_refex =~ m(^refs/) or $branch_refex =~ s(^)(refs/heads/);
} else {
$branch_refex = 'refs/.*';
}
exit 0 unless $ref =~ /^$branch_refex/;
# -- end duplication --
# we can't run this check for tag creation or new branch creation, because
# 'git log' does not deal well with $oldsha = '0' x 40.
if ( $oldsha eq "0" x 40 or $newsha eq "0" x 40 ) {
print STDERR "ref create/delete ignored for purposes of merge-check\n";
exit 0;
}
my $ret = `git rev-list -n 1 --merges $oldsha..$newsha`;
print "$refex FATAL: merge commits not allowed\n" if $ret =~ /./;
exit 0;

80
src/VREF/VOTES Executable file
View file

@ -0,0 +1,80 @@
#!/bin/sh
# gitolite VREF to count votes before allowing pushes to certain branches.
# This approximates gerrit's voting (but it is SHA based; I believe Gerrit is
# more "changeset" based). Here's how it works:
# - A normal developer "bob" proposes changes to master by pushing a commit to
# "pers/bob/master", then informs the voting members by email.
# - Some or all of the voting members fetch and examine the commit. If they
# approve, they "vote" for the commit like so. For example, say voting
# member "alice" fetched bob's proposed commit into "bob-master" on her
# clone, then tested or reviewed it. She would approve it by running:
# git push origin bob-master:votes/alice/master
# - Once enough votes have been tallied (hopefully there is normal team
# communication that says "hey I approved your commit", or it can be checked
# by 'git ls-remote origin' anyway), Bob, or any developer, can push the
# same commit (same SHA) to master and the push will succeed.
# - Finally, a "trusted" developer can push a commit to master without
# worrying about the voting restriction at all.
# The config for this example would look like this:
# repo foo
# # allow personal branches (to submit proposed changes)
# RW+ pers/USER/ = @devs
# - pers/ = @all
#
# # allow only voters to vote
# RW+ votes/USER/ = @voters
# - votes/ = @all
#
# # normal access rules go here; should allow *someone* to push master
# RW+ = @devs
#
# # 2 votes required to push master, but trusted devs don't have this restriction
# RW+ VREF/VOTES/2/master = @trusted-devs
# - VREF/VOTES/2/master = @devs
# Note: "2 votes required to push master" means at least 2 refs matching
# "votes/*/master" have the same SHA as the one currently being pushed.
# ----------------------------------------------------------------------
# see gitolite docs for what the first 7 arguments mean
# inputs:
# arg-8 is a number; see below
# arg-9 is a simple branch name (i.e., "master", etc). Currently this code
# does NOT do vote counting for branch names with more than one component
# (like foo/bar).
# outputs (STDOUT)
# nothing
# exit status:
# always 0
die() { echo "$@" >&2; exit 1; }
[ -z "$8" ] && die "not meant to be run manually"
ref=$1
newsha=$3
refex=$7
votes_needed=$8
branch=$9
# nothing to do if the branch being pushed is not "master" (using our example)
[ "$ref" = "refs/heads/$branch" ] || exit 0
# find how many votes have come in
votes=`git for-each-ref refs/heads/votes/*/$branch | grep -c $newsha`
# send back a vref if we don't have the minimum votes needed. For trusted
# developers this will invoke the RW+ rule and pass anyway, but for others it
# will invoke the "-" rule and fail.
[ $votes -ge $votes_needed ] || echo $refex "require at least $votes_needed votes to push $branch"
exit 0

36
src/VREF/lock Executable file
View file

@ -0,0 +1,36 @@
#!/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

41
src/VREF/partial-copy Executable file
View file

@ -0,0 +1,41 @@
#!/bin/sh
# push updated branches back to the "main" repo.
# This must be run as the *last* VREF, though it doesn't matter what
# permission you give to it
die() { echo "$@" >&2; exit 1; }
repo=$GL_REPO
user=$GL_USER
ref=$1 # we're running like an update hook
old=$2
new=$3
# never send any STDOUT back, to avoid looking like a ref. If we fail, git
# will catch it by our exit code
exec >&2
main=`git config --file $GL_REPO_BASE/$repo.git/config --get gitolite.partialCopyOf`;
[ -z "$main" ] && exit 0
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
git update-ref -d refs/partial/br-$rand
git update-ref $ref $new $old || die "FATAL: update-ref for $ref failed"
exit 0

75
src/VREF/refex-expr Executable file
View file

@ -0,0 +1,75 @@
#!/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.

131
src/commands/D Executable file
View file

@ -0,0 +1,131 @@
#!/bin/sh
# ----------------------------------------------------------------------
# ADMINISTRATOR NOTES:
# ----------------------------------------------------------------------
# - set TRASH_CAN in the rc if you don't like the default. It should be
# relative to GL_REPO_BASE or an absolute value. It should also be on the
# same filesystem as GL_REPO_BASE, otherwise the 'mv' will take too long.
# - you could set TRASH_SUFFIX also but I recomend you leave it as it is
# - 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).
#
# 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
# limit that your admin will tell you).
#
# The 'rm', 'lock', and 'unlock' subcommands:
# Initially, all repos are "locked" against 'rm'. The correct sequence is
# ssh git@host D unlock repo
# ssh git@host D rm repo
# Since the initial condition is always locked, the "lock" command is
# rarely used but it is there if you want it.
#
# The 'trash', 'list-trash', and 'restore' subcommands:
# You can 'trash' a repo, which moves it to a special place:
# ssh git@host D trash repo
# You can then 'list-trash'
# ssh git@host D list-trash
# which prints something like
# repo/2012-04-11_05:58:51
# allowing you to restore by saying
# ssh git@host D restore repo/2012-04-11_05:58:51
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
# ----------------------------------------------------------------------
RB=`gitolite query-rc GL_REPO_BASE`; cd $RB
TRASH_CAN=`gitolite query-rc TRASH_CAN`; tcan=Trash; TRASH_CAN=${TRASH_CAN:-$tcan}
TRASH_SUFFIX=`gitolite query-rc TRASH_SUFFIX`; tsuf=`date +%Y-%m-%d_%H:%M:%S`; TRASH_SUFFIX=${TRASH_SUFFIX:-$tsuf}
# ----------------------------------------------------------------------
owner_or_die() {
gitolite creator "$repo" $GL_USER || die You are not authorised
}
# ----------------------------------------------------------------------
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
echo "'$repo' is now gone!"
elif [ "$cmd" = "lock" ]
then
owner_or_die
rm -f $repo.git/gl-rm-ok
echo "'$repo' is now locked"
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"
elif [ "$cmd" = "trash" ]
then
owner_or_die
mkdir -p $TRASH_CAN/$repo 2>/dev/null || die "failed creating directory in trashcan"
[ -d $TRASH_CAN/$repo/$TRASH_SUFFIX ] && die "try again in a few seconds..."
mv $repo.git $TRASH_CAN/$repo/$TRASH_SUFFIX
echo "'$repo' moved to trashcan"
elif [ "$cmd" = "list-trash" ]
then
cd $TRASH_CAN 2>/dev/null || exit 0
find . -name gl-creator | sort | while read t
do
owner=
owner=`cat "$t"`
[ "$owner" = "$GL_USER" ] && dirname $t
done | cut -c3-
elif [ "$cmd" = "restore" ]
then
owner=
owner=`cat $TRASH_CAN/$repo/gl-creator 2>/dev/null`
[ "$owner" = "$GL_USER" ] || die "'$repo' is not yours!"
cd $TRASH_CAN
realrepo=`dirname $repo`
[ -d $RB/$realrepo.git ] && die "'$realrepo' already exists"
mv $repo $RB/$realrepo.git
echo "'$repo' restored to '$realrepo'"
else
die "unknown subcommand '$cmd'"
fi

74
src/commands/access Executable file
View file

@ -0,0 +1,74 @@
#!/usr/bin/perl
use strict;
use warnings;
use lib $ENV{GL_LIBDIR};
use Gitolite::Rc;
use Gitolite::Common;
use Gitolite::Conf::Load;
=for usage
Usage: gitolite access [-q] <repo> <user> <perm> <ref>
Print access rights for arguments given. The string printed has the word
DENIED in it if access was denied. With '-q', returns only an exit code
(shell truth, not perl truth -- 0 is success).
- repo: mandatory
- user: mandatory
- perm: defauts to '+'. Valid values: R, W, +, C, D, M
- ref: defauts to 'any'. See notes below
Notes:
- ref: Any fully qualified ref ('refs/heads/master', not 'master') is fine.
The 'any' ref is special -- it ignores deny rules (see docs for what this
means and exceptions).
For each case where access is not denied, one line is printed like this:
reponame<tab>username<tab>access rights
This is orders of magnitude faster than running the command multiple times;
you'll notice if you have more than a hundred or so repos.
Advanced uses: see src/triggers/post-compile/update-git-daemon-access-list for
a good example.
=cut
usage() if not @ARGV or $ARGV[0] eq '-h';
my $quiet = 0;
if ( $ARGV[0] eq '-q' ) { $quiet = 1; shift @ARGV; }
my ( $repo, $user, $aa, $ref ) = @ARGV;
$aa ||= '+';
$ref ||= 'any';
_die "invalid perm" if not( $aa and $aa =~ /^(R|W|\+|C|D|M|\^C)$/ );
_die "invalid ref name" if not( $ref and $ref =~ $REPONAME_PATT );
my $ret = '';
if ( $repo ne '%' and $user ne '%' ) {
# single repo, single user; no STDIN
$ret = access( $repo, $user, $aa, $ref );
if ( $ret =~ /DENIED/ ) {
print "$ret\n" unless $quiet;
exit 1;
}
print "$ret\n" unless $quiet;
exit 0;
}
$repo = '' if $repo eq '%';
$user = '' if $user eq '%';
_die "'-q' doesn't go with using a pipe" if $quiet;
@ARGV = ();
while (<>) {
my @in = split;
my $r = $repo || shift @in;
my $u = $user || shift @in;
$ret = access( $r, $u, $aa, $ref );
print "$r\t$u\t$ret\n";
}

15
src/commands/create Executable file
View file

@ -0,0 +1,15 @@
#!/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

40
src/commands/creator Executable file
View file

@ -0,0 +1,40 @@
#!/usr/bin/perl
use strict;
use warnings;
use lib $ENV{GL_LIBDIR};
use Gitolite::Rc;
use Gitolite::Common;
use Gitolite::Conf::Load;
=for usage
Usage: gitolite creator [-n] <reponame> [<username>]
Print the creator name for the repo. A '-n' suppresses the newline.
When an optional username is supplied, it checks if the user is the creator of
the repo and returns an exit code (shell truth, 0 for success) instead of
printing anything, which makes it possible to do this in shell:
if gitolite creator someRepo someUser
then
...
=cut
usage() if not @ARGV or $ARGV[0] eq '-h';
my $nl = "\n";
if ( $ARGV[0] eq '-n' ) {
$nl = '';
shift;
}
my $repo = shift;
my $user = shift || '';
my $creator = '';
$creator = creator($repo) if not repo_missing($repo);
if ($user) {
exit 0 if $creator eq $user;
exit 1;
}
return ( $creator eq $user ) if $user;
print "$creator$nl";

43
src/commands/desc Executable file
View file

@ -0,0 +1,43 @@
#!/bin/sh
# Usage: ssh git@host desc <repo>
# ssh git@host desc <repo> <description string>
#
# Show or set description for user-created ("wild") repo.
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
# this shell script takes arguments that are completely under the user's
# control, so make sure you quote those suckers!
# kernel.org needs 'desc' to be available to people who have "RW" or above,
# not just the "creator". In fact they need it for non-wild repos so there
# *is* no creator.
if gitolite query-rc -q WRITER_CAN_UPDATE_DESC
then
gitolite access -q "$repo" $GL_USER W any || die You are not authorised
else
gitolite creator "$repo" $GL_USER || die You are not authorised
fi
# if it passes, $repo is a valid repo name so it is known to contain only sane
# characters. This is because 'gitolite creator' return true only if there
# *is* a repo of that name and it has a gl-creator file that contains the same
# text as $GL_USER.
descfile=`gitolite query-rc GL_REPO_BASE`/"$repo".git/description
if [ -z "$1" ]
then
[ -r "$descfile" ] && cat "$descfile"
exit 0
fi
echo "$*" > "$descfile"

59
src/commands/fork Executable file
View file

@ -0,0 +1,59 @@
#!/bin/sh
# Usage: ssh git@host fork <repo1> <repo2>
#
# Forks repo1 to repo2. You must have read permissions on repo1, and create
# ("C") permissions for repo2, which of course must not exist.
#
# A fork is functionally the same as cloning repo1 to a client and pushing it
# to a new repo2. It's just a little more efficient, not just in network
# 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.
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
# ----------------------------------------------------------------------
from=$1; shift
to=$1; shift
[ -z "$to" ] && usage
gitolite access -q "$from" $GL_USER R any || die "'$from' does not exist or you are not allowed to read it"
gitolite access -q "$to" $GL_USER ^C any || die "'$to' already exists or you are not allowed to create it"
# ----------------------------------------------------------------------
# IMPORTANT NOTE: checking whether someone can create a repo is done as above.
# However, make sure that the env var GL_USER is set, and that too to the same
# value as arg-2 of the access command), otherwise it won't work.
# Ideally, you'll leave such code to me. There's a reason ^C is not listed in
# the help message for 'gitolite access'.
# ----------------------------------------------------------------------
# clone $from to $to
git clone --bare -l $GL_REPO_BASE/$from.git $GL_REPO_BASE/$to.git
[ $? -ne 0 ] && exit 1
echo "$from forked to $to" >&2
# fix up creator, default role permissions (gl-perms), and hooks
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
fi
ln -sf `gitolite query-rc GL_ADMIN_BASE`/hooks/common/* hooks
# record where you came from
echo "$from" > gl-forked-from
# trigger post_create
gitolite trigger POST_CREATE $to $GL_USER fork

86
src/commands/git-config Executable file
View file

@ -0,0 +1,86 @@
#!/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;
=for usage
Usage: gitolite git-config [-n] [-q] [-r] <repo> <key|pattern>
Print git config keys and values for the given repo. The key is either a full
key, or, if '-r' is supplied, a regex that is applied to all available keys.
-q exit code only (shell truth; 0 is success)
-n suppress trailing newline when used as key (not pattern)
-r treat key as regex pattern (unanchored)
Examples:
gitolite git-config repo gitweb.owner
gitolite git-config -q repo gitweb.owner
gitolite git-config -r repo gitweb
When the key is treated as a pattern, prints:
reponame<tab>key<tab>value<newline>
Otherwise the output is just the value.
Finally, see the advanced use section of 'gitolite access -h' -- you can do
something similar here also:
gitolite list-phy-repos | gitolite git-config -r % gitweb\\. | cut -f1 > ~/projects.list
=cut
usage() if not @ARGV;
my ( $help, $nonl, $quiet, $regex ) = (0) x 4;
GetOptions(
'n' => \$nonl,
'q' => \$quiet,
'r' => \$regex,
'h' => \$help,
) or usage();
my ( $repo, $key ) = @ARGV;
usage() unless $key;
my $ret = '';
if ( $repo ne '%' and $key ne '%' ) {
# single repo, single key; no STDIN
$key = "^\Q$key\E\$" unless $regex;
$ret = git_config( $repo, $key );
# if the key is not a regex, it should match at most one item
_die "found more than one entry for '$key'" if not $regex and scalar( keys %$ret ) > 1;
# unlike access, there's nothing to print if we don't find any matching keys
exit 1 unless %$ret;
if ($regex) {
map { print "$repo\t$_\t" . $ret->{$_} . "\n" } sort keys %$ret unless $quiet;
} else {
map { print $ret->{$_} . ( $nonl ? "" : "\n" ) } sort keys %$ret unless $quiet;
}
exit 0;
}
$repo = '' if $repo eq '%';
$key = '' if $key eq '%';
_die "'-q' doesn't go with using a pipe" if $quiet;
@ARGV = ();
while (<>) {
my @in = split;
my $r = $repo || shift @in;
my $k = $key || shift @in;
$k = "^\Q$k\E\$" unless $regex;
$ret = git_config( $r, $k );
next unless %$ret;
map { print "$r\t$_\t" . $ret->{$_} . "\n" } sort keys %$ret;
}

42
src/commands/help Executable file
View file

@ -0,0 +1,42 @@
#!/usr/bin/perl
use strict;
use warnings;
use lib $ENV{GL_LIBDIR};
use Gitolite::Rc;
use Gitolite::Common;
=for usage
Usage: ssh git@host help # via ssh
gitolite help # directly on server command line
Prints a list of custom commands available at this gitolite installation.
Each command has its own help, accessed by passing it '-h' again.
=cut
usage() if @ARGV;
my $user = $ENV{GL_USER} || '';
print "hello" . ( $user ? " $user" : "" ) . ", this is gitolite3 " . version() . " on git " . substr( `git --version`, 12 ) . "\n";
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}{$_};
}
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`;
}

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