2009-08-27 11:54:23 +02:00
|
|
|
# assorted faqs, tips, and notes on gitolite
|
|
|
|
|
2009-08-28 15:39:31 +02:00
|
|
|
In this document:
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
* <a href="#_common_errors_and_mistakes">common errors and mistakes</a>
|
|
|
|
* <a href="#_git_version_dependency">git version dependency</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="#_umask_setting">umask setting</a>
|
|
|
|
* <a href="#_getting_a_tar_file_from_a_clone">getting a tar file from a clone</a>
|
|
|
|
* <a href="#_features">features</a>
|
|
|
|
* <a href="#_syntax_and_normal_usage">syntax and normal usage</a>
|
|
|
|
* <a href="#_simpler_syntax">simpler syntax</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="#_exclude_or_deny_rules">"exclude" (or "deny") rules</a>
|
|
|
|
* <a href="#_separating_delete_and_rewind_rights">separating delete and rewind rights</a>
|
|
|
|
* <a href="#_separating_create_and_push_rights">separating create and push rights</a>
|
|
|
|
* <a href="#_file_dir_NAME_based_restrictions">file/dir NAME based restrictions</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="#_including_config_lines_from_other_files">including config lines from other files</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="#_INconvenience_features">INconvenience features</a>
|
|
|
|
* <a href="#_deleting_a_repo">deleting a repo</a>
|
|
|
|
* <a href="#_helping_with_gitweb">helping with gitweb</a>
|
|
|
|
* <a href="#_easier_to_specify_gitweb_description_and_gitweb_daemon_access">easier to specify gitweb "description" and gitweb/daemon access</a>
|
|
|
|
* <a href="#_easier_to_link_gitweb_authorisation_with_gitolite">easier to link gitweb authorisation with gitolite</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>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
----
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_common_errors_and_mistakes"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
### common errors and mistakes
|
2009-09-11 19:33:41 +02:00
|
|
|
|
|
|
|
* 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
|
2009-12-10 12:37:46 +01:00
|
|
|
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.
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
Please see doc/ssh-troubleshooting.mkd for what all this means.
|
2009-09-11 19:33:41 +02:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_git_version_dependency"></a>
|
2009-09-21 04:18:30 +02:00
|
|
|
|
2010-05-21 14:23:05 +02:00
|
|
|
### git version dependency
|
2009-12-10 12:37:46 +01:00
|
|
|
|
2010-05-21 14:23:05 +02:00
|
|
|
Gitolite (on the server) now refuses to run if git is not at least 1.6.2.
|
2009-12-10 12:37:46 +01:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_other_errors_warnings_notes_"></a>
|
2009-12-10 12:37:46 +01:00
|
|
|
|
2010-05-21 14:23:05 +02:00
|
|
|
### other errors, warnings, notes...
|
2009-12-10 12:37:46 +01:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_cloning_an_empty_repo"></a>
|
2009-08-27 11:54:23 +02:00
|
|
|
|
2010-05-21 14:23:05 +02:00
|
|
|
#### cloning an empty repo
|
2009-08-27 11:54:23 +02:00
|
|
|
|
2009-12-21 18:28:47 +01:00
|
|
|
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]
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_all_syntax_for_repos"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
#### `@all` syntax for repos
|
2009-12-21 18:28:47 +01:00
|
|
|
|
|
|
|
There *is* a way to use the `@all` syntax for repos also, as described in
|
2010-03-23 17:50:34 +01:00
|
|
|
`conf/example.conf`. However, there are a couple of minor cautions:
|
2009-12-21 18:28:47 +01:00
|
|
|
|
2010-03-23 17:50:34 +01:00
|
|
|
* 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.
|
|
|
|
* don't try giving `@all` users some permission for `@all` repos
|
2009-12-21 18:28:47 +01:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_umask_setting"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
#### umask setting
|
2009-12-21 18:28:47 +01:00
|
|
|
|
|
|
|
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
|
2009-09-15 08:34:15 +02:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_getting_a_tar_file_from_a_clone"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
### getting a tar file from a clone
|
2009-10-11 05:01:59 +02:00
|
|
|
|
|
|
|
You can clone the repo from github or indefero, then execute a make command to
|
|
|
|
extract a tar file of the branch you want. Please use the make command, not a
|
|
|
|
plain "git archive", because the Makefile adds a file called
|
|
|
|
`.GITOLITE-VERSION` that will help you identify which version you are using.
|
|
|
|
|
|
|
|
git clone git://github.com/sitaramc/gitolite.git
|
|
|
|
# (OR)
|
|
|
|
git clone git://sitaramc.indefero.net/sitaramc/gitolite.git
|
|
|
|
cd gitolite
|
|
|
|
make master.tar
|
2010-02-05 11:30:47 +01:00
|
|
|
# or maybe "make pu.tar"
|
2009-10-11 05:01:59 +02:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_features"></a>
|
2009-10-27 05:17:06 +01:00
|
|
|
|
2010-05-21 14:23:05 +02:00
|
|
|
### features
|
2009-08-28 15:39:31 +02:00
|
|
|
|
2010-02-16 00:27:14 +01:00
|
|
|
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
|
2010-05-21 14:23:05 +02:00
|
|
|
features than the original goal of branch-level access control.
|
2010-02-16 00:27:14 +01:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_syntax_and_normal_usage"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
#### syntax and normal usage
|
2009-10-30 16:55:06 +01:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_simpler_syntax"></a>
|
2010-02-16 00:27:14 +01:00
|
|
|
|
2010-05-21 14:23:05 +02:00
|
|
|
##### simpler syntax
|
2009-09-25 08:47:33 +02:00
|
|
|
|
|
|
|
The basic syntax is simpler and cleaner but it goes beyond that: **you can
|
|
|
|
specify access in bits and pieces**, even if they overlap.
|
|
|
|
|
|
|
|
Some access needs are best grouped by repo, some by username, and some by
|
|
|
|
both. So just do all of them, and gitolite will combine all the access lists!
|
|
|
|
Here's an example:
|
|
|
|
|
|
|
|
# define groups of people
|
|
|
|
@bosses = phb1 phb2 phb3
|
|
|
|
@devs = dev1 dev2 dev3
|
|
|
|
@interns = int1 int2 int3
|
|
|
|
|
|
|
|
# define groups of projects
|
|
|
|
@open = git gitolite linux rakudo
|
|
|
|
@closed = c1 c2 c3
|
|
|
|
@topsecret = ts1 ts2 ts3
|
|
|
|
|
|
|
|
# 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
|
|
|
|
|
|
|
|
[...or any other combination you want...]
|
|
|
|
|
|
|
|
# later in the file:
|
|
|
|
|
|
|
|
# specify access for individual repos (like RW, RW+, etc)
|
|
|
|
repo c1
|
|
|
|
[...]
|
|
|
|
|
|
|
|
[...etc...]
|
|
|
|
|
|
|
|
If you notice that `@bosses` are given read access to `@open` via both rules,
|
|
|
|
do not worry that this causes some duplication or inefficiency. It doesn't
|
|
|
|
:-)
|
|
|
|
|
|
|
|
See the "specify gitweb/daemon access" section below for one more example.
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="multikeys"></a>
|
|
|
|
|
|
|
|
<a name="_one_user_many_keys"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### one user, many keys
|
2010-02-11 04:33:26 +01:00
|
|
|
|
|
|
|
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?
|
|
|
|
|
2010-05-20 13:38:21 +02:00
|
|
|
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.
|
2010-02-11 04:33:26 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_security_access_control_and_auditing"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
#### security, access control, and auditing
|
2010-02-11 04:33:26 +01:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_two_levels_of_access_rights_checking"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### two levels of access rights checking
|
2009-09-14 09:01:19 +02:00
|
|
|
|
|
|
|
Gitolite has two levels of access checks. The **first check** is what I will
|
2010-05-21 14:23:05 +02:00
|
|
|
call the **pre-git** level. At this stage, the `gl-auth-command` has been
|
|
|
|
invoked by `sshd`, and it knows just three things:
|
2009-09-14 09:01:19 +02:00
|
|
|
|
|
|
|
* 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.
|
|
|
|
|
2010-01-30 04:05:43 +01:00
|
|
|
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.
|
2009-09-14 09:01:19 +02:00
|
|
|
|
|
|
|
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
|
2009-09-16 16:22:03 +02:00
|
|
|
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.
|
2009-09-14 09:01:19 +02:00
|
|
|
|
2009-12-01 02:45:05 +01:00
|
|
|
Gitolite also allows "exclude" or "deny" rules. See later in this document
|
|
|
|
for details.
|
2009-10-11 06:45:55 +02:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_better_logging"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### better logging
|
2009-08-28 15:39:31 +02:00
|
|
|
|
2009-10-11 05:01:59 +02:00
|
|
|
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.
|
2009-08-28 15:39:31 +02:00
|
|
|
|
2009-10-11 05:01:59 +02:00
|
|
|
> [`*`] 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" ;-)]
|
2009-08-28 15:39:31 +02:00
|
|
|
|
2009-10-11 05:01:59 +02:00
|
|
|
The log lines look like this:
|
2009-08-28 15:39:31 +02:00
|
|
|
|
2009-09-20 03:07:15 +02:00
|
|
|
2009-09-19.10:24:37 + b4e76569659939 4fb16f2a88d8b5 myrepo refs/heads/master user2 refs/heads/master
|
2009-08-28 15:39:31 +02:00
|
|
|
|
|
|
|
The "+" at the start indicates a non-fast forward update, in this case from
|
2009-09-20 03:07:15 +02:00
|
|
|
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.
|
2009-08-27 11:54:23 +02:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_exclude_or_deny_rules"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### "exclude" (or "deny") rules
|
2009-08-27 11:54:23 +02:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
Here is an illustrative explanation of "deny" rules. However, please be sure
|
|
|
|
to read the "DENY/EXCLUDE RULES" section in `conf/example.conf` for important
|
|
|
|
notes/caveats before using "deny" rules.
|
2009-08-28 15:39:31 +02:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
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:
|
2009-08-28 15:39:31 +02:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
@staff = bruce whitfield martin
|
|
|
|
[... and later ...]
|
|
|
|
RW refs/tags/v[0-9] = bruce
|
|
|
|
RW refs/tags = @staff
|
2009-10-12 16:32:38 +02:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
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.
|
2009-08-28 15:39:31 +02:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
One way to fix this is to allow "excludes" -- some changes in syntax, combined
|
|
|
|
with a rigorous, ordered, interpretation would do it.
|
2009-12-08 10:33:38 +01:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
Let's recap the **existing semantics**:
|
2009-12-08 10:33:38 +01:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
> the first matching refex that has the permission you're looking for (`W`
|
|
|
|
> or `+`), results in success. A fallthrough results in failure
|
2009-12-08 10:33:38 +01:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
Here are the **new semantics**, with changes from the "main" one in bold:
|
2009-12-08 10:33:38 +01:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
> 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
|
2009-12-08 10:33:38 +01:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
So the example we started with becomes, if you use "deny" rules:
|
2009-12-08 10:33:38 +01:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
RW refs/tags/v[0-9] = bruce
|
|
|
|
- refs/tags/v[0-9] = @staff
|
|
|
|
RW refs/tags = @staff
|
2009-12-08 10:33:38 +01:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
And here's how it works:
|
2009-10-13 06:32:45 +02:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
* 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
|
2009-10-13 06:32:45 +02:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_separating_delete_and_rewind_rights"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### separating delete and rewind rights
|
2010-03-30 16:31:49 +02:00
|
|
|
|
|
|
|
Since the beginning, `RW+` meant being able to rewind *or* delete a ref. My
|
|
|
|
stand is that these two are fairly similar, and infact 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 them --
|
|
|
|
situations where one of them should be restricted more than the other.
|
|
|
|
([Arguments][sdrr] exist for both sides: restrict delete more than rewind, and
|
|
|
|
vice versa).
|
|
|
|
|
2010-04-15 03:02:39 +02:00
|
|
|
So we now allow these two rights to be separated. Here's how:
|
2010-03-30 16:31:49 +02:00
|
|
|
|
2010-04-15 03:02:39 +02:00
|
|
|
* branch deletion is permitted by using `RWD` or `RW+D` -- essentially the
|
|
|
|
current branch permissions with a `D` suffixed
|
|
|
|
* if a repo has a rule containing such a `D`, all `RW+` permissions (for
|
|
|
|
that repo) cease to permit deletion of the ref matched.
|
|
|
|
|
|
|
|
This provides the *greatest* backward compatibility, while also enabling the
|
|
|
|
new semantics at the granularity of a repo, instead of the entire config.
|
2010-03-30 16:31:49 +02:00
|
|
|
|
2010-03-31 03:15:29 +02:00
|
|
|
Note 1: if you find that `RW+` no longer allows deletion but you can't see a
|
|
|
|
`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 :)
|
|
|
|
|
|
|
|
Note 2: a quick way to make this the default for *all* your repos is:
|
|
|
|
|
|
|
|
repo @all
|
2010-04-15 03:02:39 +02:00
|
|
|
RWD dummy-branch = foo
|
2010-03-31 03:15:29 +02:00
|
|
|
|
|
|
|
where foo can be either the administrator, or if you can ignore the warning
|
|
|
|
message when you push, a non-existant user.
|
|
|
|
|
2010-06-18 16:44:40 +02:00
|
|
|
Note 3: you can combine this with the "create a branch" permissions described
|
|
|
|
in the next section, as the example line in conf/example.conf shows.
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_separating_create_and_push_rights"></a>
|
2010-06-18 16:44:40 +02:00
|
|
|
|
|
|
|
##### separating create and push rights
|
|
|
|
|
|
|
|
This feature is similar in spirit to the previous one, so please read that
|
|
|
|
section for a general understanding.
|
|
|
|
|
|
|
|
Briefly:
|
|
|
|
|
|
|
|
* branch creation is permitted by using `RWC` or `RW+C` -- essentially the
|
|
|
|
current branch permissions with a `C` suffixed
|
|
|
|
* if a repo has a rule containing such a `C`, then the `RW` and `RW+`
|
|
|
|
permissions (for that repo) no longer permit creation of the ref matched;
|
|
|
|
they will only allow pushing to an existing ref
|
|
|
|
|
|
|
|
Note: you can combine this with the "delete a branch" permissions described in
|
|
|
|
the previous section, as the example line in conf/example.conf shows.
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_file_dir_NAME_based_restrictions"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### file/dir NAME based restrictions
|
2009-10-13 06:32:45 +02:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
In addition to branch-name based restrictions, gitolite also allows you to
|
|
|
|
restrict what files or directories can be involved in changes being pushed.
|
|
|
|
This basically uses `git diff --name-only` to obtain the list of files being
|
|
|
|
changed, treating each filename as a "ref" to be matched.
|
2009-10-13 06:32:45 +02:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
Please see `conf/example.conf` for syntax and examples.
|
2009-10-13 06:32:45 +02:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_delegating_parts_of_the_config_file"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### delegating parts of the config file
|
2009-10-13 06:32:45 +02:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
You can now split up the config file and delegate the authority to specify
|
2010-09-02 15:45:32 +02:00
|
|
|
access control for their own pieces. See [delegation][] for details.
|
2010-02-11 04:33:26 +01:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_convenience_features"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
#### convenience features
|
2009-08-28 15:39:31 +02:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_what_repos_do_I_have_access_to_"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### what repos do I have access to?
|
2009-08-28 15:39:31 +02:00
|
|
|
|
2009-10-28 09:03:24 +01:00
|
|
|
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.
|
2009-08-28 15:39:31 +02:00
|
|
|
|
2010-08-21 13:32:12 +02:00
|
|
|
Gitolite provides two commands (`info` and `expand`) to help you find this
|
|
|
|
information; please check [doc/report-output.mkd][repout] for details.
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_including_config_lines_from_other_files"></a>
|
2009-12-01 02:45:05 +01:00
|
|
|
|
2010-05-21 14:23:05 +02:00
|
|
|
##### including config lines from other files
|
2009-12-01 02:45:05 +01:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
See the entry under "INCLUDE SOME OTHER FILE" in `conf/example.conf`.
|
2009-12-01 02:45:05 +01:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_support_for_git_installed_outside_default_PATH"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### support for git installed outside default PATH
|
2009-12-01 02:45:05 +01:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
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 :-)
|
2009-12-01 02:45:05 +01:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
And if you don't have root, you can't do this anyway.
|
2009-12-01 02:45:05 +01:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
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...
|
2009-12-01 02:45:05 +01:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
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.
|
2009-12-01 02:45:05 +01:00
|
|
|
|
2010-02-11 04:33:26 +01:00
|
|
|
Very easy, very simple, and completely transparent to the users :-)
|
2009-12-01 02:45:05 +01:00
|
|
|
|
2010-04-14 19:46:29 +02:00
|
|
|
**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}";
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_personal_branches"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### "personal" branches
|
2009-08-28 15:39:31 +02:00
|
|
|
|
2009-10-11 05:01:59 +02:00
|
|
|
"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.
|
2009-08-28 15:39:31 +02:00
|
|
|
|
2010-03-16 06:07:33 +01:00
|
|
|
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.
|
2009-09-16 18:55:32 +02:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_custom_hooks_and_custom_git_config"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### custom hooks and custom git config
|
2009-12-09 07:46:22 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
`conf/example.conf` for details.
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_bypassing_gitolite"></a>
|
2010-07-25 06:01:24 +02:00
|
|
|
|
|
|
|
##### 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
|
|
|
|
|
2010-10-23 18:28:18 +02:00
|
|
|
**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.
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_INconvenience_features"></a>
|
2010-06-15 19:31:22 +02:00
|
|
|
|
|
|
|
#### INconvenience features
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_deleting_a_repo"></a>
|
2010-06-15 19:31:22 +02:00
|
|
|
|
|
|
|
##### 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`)
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_helping_with_gitweb"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
#### helping with gitweb
|
2010-02-11 04:33:26 +01:00
|
|
|
|
|
|
|
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!
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="gwd"></a>
|
|
|
|
|
|
|
|
<a name="_easier_to_specify_gitweb_description_and_gitweb_daemon_access"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### easier to specify gitweb "description" and gitweb/daemon access
|
2010-02-11 04:33:26 +01:00
|
|
|
|
2010-10-03 05:18:59 +02:00
|
|
|
Please see [gwd] for details on how to do this if you've never done this
|
|
|
|
before. This section is only about how gitolite makes it easy to specify
|
|
|
|
different combinations of access for different sets of repos.
|
2010-02-11 04:33:26 +01:00
|
|
|
|
2010-10-03 05:18:59 +02:00
|
|
|
[gwd]: http://github.com/sitaramc/gitolite/blob/pu/doc/2-admin.mkd#gwd
|
2010-02-11 04:33:26 +01:00
|
|
|
|
|
|
|
Remember gitolite lets you specify the access control specs in bits and
|
|
|
|
pieces, so you can keep all the daemon/gitweb access in one place, even if
|
|
|
|
each repo has more specific branch-level access config specified elsewhere.
|
|
|
|
Here's an example, using really short reponames because I'm lazy:
|
|
|
|
|
|
|
|
# maybe near the top of the file, for ease of access:
|
|
|
|
|
|
|
|
@only_web = r1 r2 r3
|
|
|
|
@only_daemon = r4 r5 r6
|
|
|
|
@web_and_daemon = r7 r8 r9
|
|
|
|
|
|
|
|
repo @only_web
|
|
|
|
R = gitweb
|
|
|
|
repo @only_daemon
|
|
|
|
R = daemon
|
|
|
|
repo @web_and_daemon
|
|
|
|
R = gitweb
|
|
|
|
R = daemon
|
|
|
|
|
|
|
|
# ...maybe much later in the file:
|
|
|
|
|
|
|
|
repo r1
|
|
|
|
# normal developer access lists for r1 and its branches/tags in the
|
|
|
|
# usual way
|
|
|
|
|
|
|
|
repo r2
|
|
|
|
# ...and so on...
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_easier_to_link_gitweb_authorisation_with_gitolite"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### easier to link gitweb authorisation with gitolite
|
2010-02-11 04:33:26 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2010-05-14 18:00:40 +02:00
|
|
|
Of course some other authentication method can be used (e.g. `mod_ldap`) as
|
|
|
|
long as the usernames match.
|
2010-02-11 04:33:26 +01:00
|
|
|
|
|
|
|
Gitweb allows you to specify a subroutine to decide on access. We use that
|
2010-05-14 18:00:40 +02:00
|
|
|
feature and tie it to gitolite. Configuration example can be found in
|
|
|
|
`contrib/gitweb/`.
|
2010-02-11 04:33:26 +01:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_advanced_features"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
#### advanced features
|
2010-02-11 04:33:26 +01:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_repos_named_with_wildcards"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### repos named with wildcards
|
2009-12-06 07:11:02 +01:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
Please see `doc/wildcard-repositories.mkd` for all the details.
|
2009-12-06 07:11:02 +01:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_admin_defined_commands"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### admin defined commands
|
2010-04-27 11:12:05 +02:00
|
|
|
|
|
|
|
This requires the wildcards feature to be enabled, but is then an extremely
|
|
|
|
powerful feature. See `doc/admin-defined-commands.mkd`.
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_access_control_for_external_commands"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
##### access control for external commands
|
2010-01-31 15:54:36 +01:00
|
|
|
|
|
|
|
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
|
2010-05-10 03:55:23 +02:00
|
|
|
decision.
|
2010-01-31 15:54:36 +01:00
|
|
|
|
|
|
|
Note that this is incompatible with giving people shell access as described in
|
2010-09-02 15:45:32 +02:00
|
|
|
`doc/ssh-troubleshooting.mkd` -- people who have shell access are not
|
2010-01-31 15:54:36 +01:00
|
|
|
subject to this mechanism (it wouldn't make sense to try and control someone
|
|
|
|
who has shell access anyway).
|
|
|
|
|
2010-05-10 03:55:23 +02:00
|
|
|
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)
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_svnserve"></a>
|
2010-05-10 03:55:23 +02:00
|
|
|
|
2010-05-21 14:23:05 +02:00
|
|
|
###### svnserve
|
2010-05-10 03:55:23 +02:00
|
|
|
|
|
|
|
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.
|
2010-01-31 15:54:36 +01:00
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_design_choices"></a>
|
2010-05-21 14:23:05 +02:00
|
|
|
|
|
|
|
### design choices
|
|
|
|
|
2010-09-02 15:45:32 +02:00
|
|
|
<a name="_keeping_the_parser_and_the_access_control_separate"></a>
|
2009-09-16 18:55:32 +02:00
|
|
|
|
2010-05-21 14:23:05 +02:00
|
|
|
#### keeping the parser and the access control separate
|
2009-10-13 06:32:58 +02:00
|
|
|
|
|
|
|
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.
|
2010-09-02 15:45:32 +02:00
|
|
|
|
|
|
|
[repout]: http://github.com/sitaramc/gitolite/blob/pu/doc/report-output.mkd
|
|
|
|
[sdrr]: http://groups.google.com/group/gitolite/browse_thread/thread/9f2b4358ce406d4c#
|
|
|
|
[delegation]: http://github.com/sitaramc/gitolite/blob/pu/doc/delegation.mkd
|
|
|
|
[rmrepo]: http://github.com/sitaramc/gitolite/blob/pu/doc/admin-defined-commands.mkd#rmrepo
|