In the previous post, Converting a CVS project to a Git repository, I describe using cvs2git to convert a CVS project to a git repository. After I made the conversion, I wanted to make the CVS project read-only.
There’s probably no reason to keep the CVS project around (the history is in the git repo, and I have backups of the CVS project), but it felt like the right thing to do. The blog post Read-only CVS access for only certain projects was extremely helpful to accomplish this.
The key component is the CVSROOT/commitinfo
file within your CVS repository. Like any other project in CVS, you need to check this out to make changes:
cvs co CVSROOT
cd CVSROOT && vi commitinfo
You specify a regular expression and a script to run before committing data to a project matching that regular expression. If the script exits with a non-zero exit code (indicating an error), the commit is aborted. For initial testing, I used false
(or /bin/false
) for the script component, which does nothing and returns an exit code of 1.
I had some problems with this, in part because I was not sure what the project string would look like. I tried a few things:
^/testrepo/.* false
(didn’t work)^testrepo/.* false
didn’t work^t.* false
worked, but would match other projects as well
Eventually I switched to using the read-only-project.sh
example from the aforementioned blog post, which printed out the values of the project path and the filenames to be committed.
From there I could see that the project path:
- Does not include an initial slash
- Does not include a trailing slash
- May include additional slashes if the project contains subdirectories
The same script suggests including the following in commitinfo:
^projectname/.* /path/to/script "%p" %s
That regular expression does not work — it would match a file at projectname/subdir1/file1 but not projectname/file1.
And what do the “%p” and %s mean? From C.3.4 Commitinfo:
Currently, if no format strings are specified, a default string of ` %r/%p %{s}’ will be appended to the command line template before replacement is performed, but this feature is deprecated.
I found another document, C.3.1 The common syntax, which describes the format strings.
p
– the name of the directory being operated on within the repository.{s}
– the file name(s), in curly braces because it is a list
The same page includes a sample regular expression that solves the problem I was having:
^module\(/\|$\)
Finally, here is what I added to CVSROOT/commitinfo
:
^testrepo\(/\|$\) /usr/local/script/read-only-project.sh
Note that this script needs to exist on the same machine as the CVS repository (which may or may not be the same machine as your checked-out copy).
I’ve made a maintenance on a cvs repo that was used by quite a few people a while back and simply created locks for this purpose with:
find /home/cvs/cvsroot/ -type d -exec touch {}/#cvs.lock \;
The substitution strings (%p, %s, etc.) are listed in the default `commitinfo` file. (Would’ve been nice if they were in the documentation, but alas.) For anyone else who needs them:
# Format strings present in the filter will be replaced as follows:
# %c = canonical name of the command being executed
# %R = the name of the referrer, if any, otherwise the value NONE
# %p = path relative to repository
# %r = repository (path portion of $CVSROOT)
# %{s} = file name, file name, …
Also, I had a helluva time trying to get this approach to work properly on CVS running in a RootJail, because, at least on my company’s installation, the example script failed because`/bin/sh` isn’t available in the jail. Copying `sh` into the jail wasn’t sufficient because it depends on various other commands and libraries that also weren’t present. Rather than go off and learn how `cvsd-buildroot` works as we’re trying to get away from CVS, I found that omitting the shebang (#!/bin/sh) at the top of the script worked as long as the script doesn’t do anything shell-specific. In my case, I just did something like this:
# Invoke from commitinfo as /myscript %r “%p”
echo Commit rejected: $1/$2 is read-only!
false