virtual refs

(part 2)

VREF-makers shipped with gitolite

restricting pushes by dir/file name

The "NAME" VREF allows you to restrict pushes by the names of dirs and files changed. (Side note: the NAME VREF is the only one directly implemented within the update hook, so you won't find it in the VREF directory).

Here's an example. Say you don't want junior developers pushing changes to the Makefile, because it's quite complex:

repo foo
        RW+                             =   @senior_devs
        RW                              =   @junior_devs

        -   VREF/NAME/Makefile          =   @junior_devs

When a senior dev pushes, the VREF is not invoked at all. But when a junior dev pushes, the VREF is invoked, and it returns a list of files changed as virtual refs, looking like this:


Each of these refs is matched against the access rules. If one of them happens to be the Makefile, then the ref returned (VREF/NAME/Makefile) will match the deny rule and kill the push.

Another way to use this is when you know what is allowed instead of what is not allowed. Let's say the QA person is only allowed to touch a file called CHANGELOG and any files in a directory called ReleaseNotes:

repo foo
        RW+                             =   @senior_devs
        RW                              =   @junior_devs
        RW+                             =   QA-guy

        RW+ VREF/NAME/CHANGELOG         =   QA-guy
        RW+ VREF/NAME/ReleaseNotes/     =   QA-guy
        -   VREF/NAME/                  =   QA-guy

number of changed or new files

The COUNT VREF is used like this:

    -   VREF/COUNT/9                    =   @junior-developers

In response, if anyone in the user list pushes a commit series that changes more than 9 files, a virtual ref of VREF/COUNT/9 is returned. Gitolite uses that as a "ref" to match against all the rules, hits the same rule that invoked it, and denies the request.

If the user did not push more than 9 files, the VREF code returns nothing, and nothing happens.

COUNT can take one more argument:

    -   VREF/COUNT/9/NEWFILES           =   @junior-developers

This is the same as before, but have to be more than 9 new files not just changed files.

advanced filetype detection

Note: this is more for illustration than use; it's rather specific to one of the projects I manage but the idea is the important thing.

Sometimes a file has a standard extension (that cannot be 'gitignore'd), but it is actually automatically generated. Here's one way to catch it:


You can look at src/VREF/FILETYPE to see how it handles the 'AUTOGENERATED' option. You could also have a more generic option, like perhaps BINARY, and handle that in the FILETYPE vref too.

checking author email

Some people want to ensure that "you can only push your own commits".

If you force it on everyone, this is a very silly idea (see "Philosophical Notes" section of src/VREF/EMAIL-CHECK).

But there may be value in enforcing it just for the junior developers.

The neat thing is that the existing contrib/ was just copied to src/VREF/EMAIL-CHECK and it works, because VREFs get the same first 3 arguments and those are all that it cares about. (Note: you have to change one subroutine in that script if you want to use it)

voting on commits

Although gitolite can't/won't do the whole "code review + workflow enforcement" thing that Gerrit Code Review does, a basic implementation of voting on a commit is surprisingly easy. See src/VREF/VOTES for details (and note that the actual code is just 2-3 lines; the rest is inline documentation).

other ideas -- code welcome!

"no non-merge first-parents"

Shruggar on #gitolite wanted this. Possible code to implement it would be something like this (untested)

[ -z "$(git rev-list --first-parent --no-merges $2..$3)" ]

This can be implemented using src/VREF/MERGE-CHECK as a model. That script does what the 'M' qualifier does in access rules (see last part of this), although the syntax to be used in conf/gitolite will be quite different.

other ideas for VREFs

Here are some more ideas:

  • Number of commits (git rev-list --count $old $new).
  • Number of binary files in commit (currently I only know to count occurrences of Bin in the output of git diff --stat.
  • Number of new binary files (count Bin 0 -> in git diff --stat output).
  • Time of day/day of week (see example snippet somewhere above).
  • IP address.
  • Phase of the moon.

Note that pretty much anything that involves $oldsha..$newsha will have to deal with the issue that when you push a new tag or branch, the "old" part is all 0's, and unless you consider --all existing branches and tags it becomes meaningless in terms of "number of new files" etc.