aboutsummaryrefslogtreecommitdiff
blob: eaefde9f7cfadf01642e63477baa5097e9227d7f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
# F=conf the access control file `gitolite.conf`

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.

## #confrecap a quick summary of the conf file

This is a quick *recap* of the *most common* elements in a typical config
file.  (In the following description, the angle brackets are not part of the
syntax).

  * (optional) [group][groups] definitions

        @<groupname> = <list of users and groups>

  * repo access rules

        repo <reponame>
            # one or more access rules like
            <permission> <optional refex> = <list of users and groups>

    The [most common][bac] permissions are:

      * R, for read only
      * RW, for push existing ref or create new ref
      * RW+, for  "push -f" or ref deletion allowed (i.e., destroy
        information)
      * `-` (the minus sign), to [deny][] access

    If no [refex][] is supplied, the access rule applies to all refs.

    Later, gitolite [acquired][rwcd]

      * RWC and RW+C, for ref creation.  Once you use this in a repo, a normal
        RW/RW+ can no longer create a ref; it can only push existing refs
        (<font color="gray">Note also that a standalone "C" permission is
        something [completely different][wild]</font>)
      * RWD and RW+D, for ref deletion.  Once you use this in a repo, a normal
        RW+ can no longer delete a ref; it can only rewind
      * (RWCD and RW+CD, which is just a combination of these two)

    Even later, [gitolite acquired][mergecheck], if you want to enforce a
    rebase-only workflow.

The following sections dig deeper into the syntax and semantics of the access
control rules and other configuration directives in the `gitolite.conf` file.
If narrative descriptions scare you, or your English is not upto scratch, try
[gitolite config by example][conf_examples] also.

## F=syntax lexical 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)

### 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.

### include files

Gitolite allows you to break up the configuration into multiple files and
include them in the main file for convenience.

    include     "foo.conf"

will include the contents of the file "foo.conf".

Details:

  * You can also use a glob (`include "*.conf"`), or put your include files
    into subdirectories of "conf" (`include "foo/bar.conf"`), or both
    (`include "repos/*.conf"`).

  * Included files are always searched from the gitolite-admin repo's "conf/"
    directory, unless you supplied an absolute path.  (Note: in the interests
    of cloning the admin-repo sanely you should avoid absolute paths!)

  * If you ended up recursing, files that have been already processed once are
    skipped, with a warning.

<font color="gray">Advanced users: `subconf`, a command that is very closely
related to `include`, is documented [here][subconf].</font>

## F=bac 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.

Note that *tags* are generally considered "write once", so they are treated
specially by gitolite: you need rewind permission (`RW+`) to *overwrite* a
tag with a new value.

In a later section you'll see some more advanced permissions.

### how rules are matched

It's important to understand that there're two levels at which access control
happens.  Please see [this][2levels] 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.

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.

### #refex 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 (as if you
    had specified `refs/.*` as the refex).

  * 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.

### #groups 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.

#### 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 the
documentation for [`~/.gitolite.rc`][rc].

When used as a reponame, it includes all repos physically present in
`~/repositories` (or whatever is the value of `$REPO_BASE`).

### F=rpr_ side note: "R" permissions for refs

You can control "read" access only at the repo level, not at the branch level.
For example, this **won't** limit Wally to reading only the master branch:

    repo foo
        R master    =   wally       # WILL NOT DO WHAT YOU THINK IT DOES!!

and this **won't** prevent him from reading it:

    repo foo
        - master    =   wally       # WILL NOT DO WHAT YOU THINK IT DOES!!
        R           =   wally

This (inability to distinguish one ref from another during a read operation)
is a git issue, not a gitolite issue.

There are 3 ways around this, though:

  * switch to gerrit, which has its own git stack, its own sshd, and God knows
    what else.  All written in Java, the COBOL of the internet era ;-)
  * bug the git people to add this feature in ;-)
  * use a separate repo for Wally.

Using separate repos is not that hard with gitolite.  Here's how to maintain a
[partial copy][partialcopy] of the main repo and keep it synced (while not
allowing the secret branches into it).

## F=aac advanced access control

The previous section is sufficient for most common needs, but gitolite can go
a lot further than that.

### #NAME restricting pushes by dir/file name using NAME/

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/".

### #deny "deny" rules

#### warnings and required reading

Gitolite performs access checks at 2 levels.  The first check is performed for
both read *and* write operations, while the second one happens only for write
operations.

**Required reading**: [this section][2levels] of the documentation.

**Warning**: When using deny rules, the order of your rules matters, 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.

#### "deny" rules for refs in a repo

You can use "deny" rules for the second check, to prevent people pushing
branches or tags that they should not be allowed to.

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

#### "deny" rules for the entire repo

The previous section described deny rules for the second check, which is a
fairly common need.  However, gitolite does not process deny rules for the
first check -- it's usually simple enough to make sure your config file does
not allow the particular user/repo combindation at all.

But there's one case where this becomes cumbersome: when you want to create
exceptions to uses of `@all`.

For example, if you want gitweb to show `@all` repos except the special
'gitolite-admin' repo, you must manually (and laboriously) maintain a list of
all your repos (except gitolite-admin) and use that in place of `@all`.  Oh
joy...

But now you can do this:

    repo gitolite-admin
        -   =   gitweb daemon
        [... other access rules ...]
        config gitolite-options.deny-repo = 1

    repo @all
        R   =   gitweb daemon

Here are some notes on how/why this works:

  * The order is important -- the deny rule must come first.

  * The 'config' line is what tells git to behave differently, i.e., apply
    deny rules for the first check also.

  * Since there is no "ref" to match against the refexes in the rules,
    gitolite just ignores the refexes, and simply looks at the permission (R,
    RW, "-", etc) and the user list.

### #rwcd 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 now supports that (in a backward compatible way).

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 permissions like
`RWC` or `RW+C` anywhere in that repo, 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.

### #mergecheck enforcing a no-merges policy

Some people want to enforce a no-merges policy for various reasons.  The new
"M" qualifier can be used to specify who is allowed to push merge commits.
This works just like "C" and "D" in the previous section, so **please read
that for a more detailed description** and apply the same ideas, (including
the `@all` trick!) to "M" for "allow merge commits".

The only other thing to remember is that this qualifier, if used, goes at the
end of any permission starting with `RW` (i.e., all of them except `R`).  For
example, `RW` becomes `RWM`, `RW+` becomes `RW+M`, `RW+CD` becomes `RW+CDM`.

## summary: permissions

The full set of permissions, in regex syntax: `-|R|RW+?C?D?M?`.  This expands
to one of `-`, `R`, `RW`, `RW+`, `RWC`, `RW+C`, `RWD`, `RW+D`, `RWCD`, or
`RW+CD`, all but the first one optionally followed by an `M`.  And by now you
know what they all mean.

[Side note: There is one more very important permission to be dealt with --
the standalone `C`, which is not really a "ref" level permission and can be
found in doc/wildcard-repositories.mkd.]

## F=confother_ other tips

### personal branches

Gitolite lets you define a "personal" or "scratch" namespace prefix for each
developer.  See [here][pers] for details.

### #ruleaccum rule accumulation

(Also see [this][ruleaccum2] for a different example that may be more
intuitive for some people).

Gitolite lets you specify access rules for a repo in bits and pieces, and
accumulates them in the same sequence they were given.  This is very
convenient.  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][deny] anywhere then the *order of the
rules matters*!

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.

### #gwd specifying gitweb and daemon access

Gitolite allows you to specify access for git-daemon and gitweb.  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"

These lines are standalone, so you can add them anywhere in the conf file.

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**.

### #rsgc repo specific `git config` commands

(Thanks to teemu dot matilainen at iki dot fi)

>   ----

>   **Note**: this won't work unless the rc file has the right settings;
>   please see `$GL_GITCONFIG_KEYS` in the [rc file doc][rc] for details and
>   security information.

>   ----

Sometimes you want to specify `git config` settings for your repos.

For example, say you have a custom post-receive hook that sends an email when
a push happens, and this hook looks in the config for whom to send the email
to, etc.

You can set these git config values within a "repo" paragraph:

    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 of the `git config` command (`--add`, the
`value_regex`, etc) are not supported.

>   ----

>   **WARNING**: simply deleting the config line from the `conf/gitolite.conf`
>   file will *not* delete the variable from `repo.git/config`.  The syntax in
>   the last example is the *only* way to make gitolite execute a
>   `--unset-all` operation on the given key.

>   ----

You can repeat the 'config' line as many times as you like, and the last
occurrence will be the one in effect.  This allows you to override settings
just for one project, as in this example:

    repo @all
        config gitolite.mirror.master = "frodo"
        config gitolite.mirror.slaves = "sam gollum"

    repo top-secret-project
        # only sam, because we don't trust gollum
        config gitolite.mirror.slaves = "sam"

The "delete config variable" syntax can also be used, if you wish:

    repo highlander     # there can be only one!
        config gitolite.mirror.master =
        config gitolite.mirror.slaves =

As you can see, the general idea is to place the most generic ones (`repo
@all`, or repo patterns like `repo foo.*`) first, and place more specific ones
later to override the generic settings.