diff --git a/contrib/gerrit.mkd b/contrib/gerrit.mkd
index 679f1a9..e55cf47 100644
--- a/contrib/gerrit.mkd
+++ b/contrib/gerrit.mkd
@@ -92,10 +92,6 @@ review stuff :)
* gitolite doesnt do anything special to signed or annotated tags
- * gitolite always allows creating a branch. The only way to prevent that is
- to list out allowed branches explicitly (make sure you end the refex with
- a `$`!).
-
* Force push is the same as delete: historically (and by default, even now)
gitolite does the same . However, I've only recently (and somewhat
reluctantly) changed gitolite to allow treating these two separately.
diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd
index 74c1c2c..fc8663f 100644
--- a/doc/3-faq-tips-etc.mkd
+++ b/doc/3-faq-tips-etc.mkd
@@ -18,6 +18,7 @@ In this document:
* better logging
* "exclude" (or "deny") rules
* separating delete and rewind rights
+ * separating create and push rights
* file/dir NAME based restrictions
* delegating parts of the config file
* convenience features
@@ -381,6 +382,40 @@ message when you push, a non-existant user.
[sdrr]: http://groups.google.com/group/gitolite/browse_thread/thread/9f2b4358ce406d4c#
+
+
+##### separating create and push rights
+
+[note: the documentation took longer to write than the code ;-)]
+
+Now you can disallow creation of new refs if you like.
+
+Normally, when you try to update a ref (push a branch or a tag), it's checked
+against all the refexes, and if none of them match for the operation you're
+trying (W or +), or it matches a "-", the operation is denied.
+
+That is, most refs are default "deny".
+
+`CREATE_REF` is a "fake" refex that controls the ability to *create* a branch,
+even if you are allowed to *push* changes to it.
+
+The right to create a ref (i.e., push a brand new one), however, defaults to
+"accept" unless a deny rule is found. This is mainly for backward compat
+reasons, but also because this feature is rarely needed, so there's no point
+burdening everyone with having to create the opposite rule.
+
+So if you want to prevent someone from *creating* any branches that they
+otherwise *can* push, make sure that the first rule that applies to them is a
+`- CREATE_REF` rule, like line #3 below:
+
+ repo foo
+ RW+ = @leads
+ - CREATE_REF = @devs
+ RW+ = @devs
+
+One side effect is that you now can no longer have an *actual* branch called
+`CREATE_REF`. Oh well, into each life some rain must fall!
+
##### file/dir NAME based restrictions
diff --git a/hooks/common/update b/hooks/common/update
index f55e2cc..219157d 100755
--- a/hooks/common/update
+++ b/hooks/common/update
@@ -82,6 +82,8 @@ push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{'@all'} || [] };
# been specified
my @refs = ($ref); # the first ref to check is the real one
+# if a new ref is being created, add a special ref to record that
+push @refs, 'refs/heads/CREATE_REF' if $oldsha eq '0' x 40;
# 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
diff --git a/src/gitolite.pm b/src/gitolite.pm
index bdc0db8..bb4fb5f 100644
--- a/src/gitolite.pm
+++ b/src/gitolite.pm
@@ -87,7 +87,7 @@ sub check_ref {
# as far as *this* ref is concerned we're ok
return $refex if ($ar->[2] =~ /\Q$perm/);
}
- die "$perm $ref $repo $ENV{GL_USER} DENIED by fallthru\n";
+ die "$perm $ref $repo $ENV{GL_USER} DENIED by fallthru\n" unless $ref eq 'refs/heads/CREATE_REF';
}
# ln -sf :-)