[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Emails on branch commits? & disallow commits by branch?

From: Mark D. Baushke
Subject: Re: Emails on branch commits? & disallow commits by branch?
Date: Wed, 08 May 2002 09:05:35 -0700

> From: Nick Papadonis <address@hidden>
> Date: Wed, 08 May 2002 10:21:09 -0400
> "Mark D. Baushke" <address@hidden> writes:
> >> Is there a way to disallow users to commit changes in certain
> >> branches?
> >
> > Yes. Return non-zero return code out of the commitinfo script when the
> > user is not allowed to commit to that branch.
> >
> Thanks Mark.
> I'm a little confused however.  Must you have a list of every user to
> excluded from a branch checkin?

Well, you need a list of users that are either in the set of allowed
or in the set of disallowed. It is up to you how to implement it.  The
code was just provided as an example approach. I would suggest you
consider your implementation in light of your individual needs.

> Wouldn't it be better to have a list of users who CAN check into a
> branch?

'Better' is a matter of policy. You could do both, or you could just
have a big lock that excludes everyone as in the case when you have
end-of-lifed development on a particular branch.

> It would seem smaller in size.

If that is true, then in the example you could just invert the sense
of the tests:

        push(@locked_tags, $branch) if (grep($login ne $_, @users));
    if (grep($_ ne $cvsbranch{$arg}, @locked_tags)) {

or come up with some way to keep a set of allowed or disallowed users
and use both of them based on other criteria such as membership in a
unix group rather than the loginid. There is more than one way to do

> My Perl is a little new, can you explain what files contain the
> usernames for the following code segment?

Sure. The only file that contains usernames is the one I was
suggesting you put into your $CVSROOT/CVSROOT/locked-tags directory.
As this file does not normally exist by default in a repository, you
would need to create it. Something like the following steps on a unix
box might do what you want:

   cvs checkout CVSROOT
   echo 'tagname user1 user2 user3 user4' > locked-tags   # add the list of 
tags and users
   cvs add locked-tags
   echo '^CVSROOT true' >> commitinfo           # first non-commentary line of 
   echo 'DEFAULT commit_prep' >> commitinfo     # last non-commentary line of 
   cvs add commit_prep
   echo 'locked-tags' >> checkoutlist  # tell cvs to checkout this file in the 
real repository
   echo 'commit_prep' >> checkoutlist
   cvs commit                          # commit your changes

The changes to commitinfo tell cvs that during a future commit, files
in the CVSROOT module should NOT be run thru the commit_prep script so
that you will never accidentally have a problem committing to the
CVSROOT module and all other modules will run the commit_prep script.
The DEFAULT tells cvs to push all of the other modules thru the
commit_prep check. You could provide a list of modules that should use
this particular commit_prep script rather than using DEFAULT if you

When the commit_prep script is run, it will be run for each directory
in your tree that contains an added, removed or modified file and will
be given as list of files on the command line. The current working
directory when the commit_prep script is run is the directory being
processed (or a shadow copy of that directory on the server).

The @files perl array is taken from the list of command line arguments
when commit_prep is run. It will be a list of simple filenames.

Note that there was an error in the patch I sent to you. You need to
adjust the name of the tag read from the CVS/Entries file so that it
does not have the leading 'T' character on it.

># Suck in the CVS/Entries file
>open(ENTRIES, $ENTRIES) || die("Cannot open $ENTRIES.\n");
>while (<ENTRIES>) {
>    local($filename, $version,$ts,$opt,$tag) = split('/', substr($_, 1));
>    $cvsversion{$filename} = $version;

    $tag =~ s/^T//;         # remove the tagname character
    $tag = 'MAIN' if ($tag eq '');

The following line looks at the branch or version tag that exists in
the cvs administration file (CVS/Entries) and saves that into a
%cvsbranch perl hash for later use. This is important to do as it is
possible to have a directory that contains a mixture of different
branches and you want to make sure that you find those that are mixed.

>    $cvsbranch{$filename} = $tag;

Note that after you have read the CVS/Entries file, you could also do
a check to make sure that there was only a consistent tag in the
checked-out files for the current directory by doing something like:

foreach $arg (@files) {
@tags = keys %branchtag;
if (scalar(@tags) > 1) {
    printf(STDERR "Warning: You are attempting to commit to %d branches:\n",
          "\t".join("\n\t", @ptags), "\n",
          "in the same directory ($directory).\n",
          "A multi-branch commit operation is not allowed.\n");
    exit 1;

However, it is a matter of local policy that you determine if it is
legal to commit to multiple branches at the same time or not...

> # Disallow commits by some users to given branches
> foreach $arg (@files) {

The next block of code ignores the check for any files that begin with
a dot in the filename. (I just quickly grabbed the block of code that
was doing the check_version() stuff from further down in the file to
code the example.)  You probably want to remove it from the example.
In most repositories I have maintained, the only dot-files that exist
are .cvsignore files, but your local policy may be different.

>     if (index($arg, ".") == 0) {
>         next;
>     }

In the original patch provided, @locked_tags contains the list of
branches that are not allowed to be committed to by the current user.

$cvsbranch{$arg} is the tag read out of the CVS/Entries file for this

The grep() function iterates over @locked_tags with an implicit for loop
setting $_ to the value of each member in the array in turn.

The perl idiom

   grep($_ eq $cvsbranch{$arg}, @locked_tags)

is roughly equivalent to


given the following representation of mygrep()

sub mygrep {
   @_= ();
   foreach $_ (@locked_tags) {
       if ($_ eq $cvsbranch{$arg}) {
           push(@_, $_);

which you may find easier to follow

However, since the grep is used in a scalar context, we only care how
many elements are returned. 

If zero elements are returned, then it means that $cvsbranch{$arg}
does not appear in the @locked_tags array and so the if-statement is

If one or more elements are returned, then it means that
$cvsbranch{$tag} was found one or more times in the @locked_tags

In other words, the if-statement grep means: Did we find any elements
in the @locked_tags array that matches the branch tag in use by file $arg ?
if so, we have a problem, inform the user and abort the commit.

>     if (grep($_ eq $cvsbranch{$arg}, @locked_tags)) {
>         print STDERR "Branch $cvsbranch{$arg} is not allowed for $login\n";
>         exit(1);
>     }
> }
> Thanks again.
> - Nick

        Good luck,
        -- Mark

reply via email to

[Prev in Thread] Current Thread [Next in Thread]