new adc to allow deleting a branch that you created; see below

The need for this comes about as follows:

  - a project may allow its developers "RWC" (or "RW+C") so that they
    can create feature branches when needed.  Note that these are
    *feature* branches, so they can't use the "personal branches"
    mechanism that gitolite already has.

  - the developers are *not* given RWCD (or RW+CD) to prevent accidental
    deletion of an important branch.  Branch *deletion* is something
    that only a few trusted admins can do.

  - as a result, there are sometimes situations where a developer
    creates a misnamed branch and then has to ask the admins to help get
    rid of it.

What the KDE folks wanted was a way to allow the creator of a branch to
be able to delete it.  In addition, they needed this allowed only for a
fixed duration after the creation of a branch, not forever (for the same
reason they don't get RWCD, to prevent accidents).

These are my reasons why this feature is implemented as an ADC instead
of being "in core":

  - we'd need additional syntax to differentiate this special case
    (which is sort of in between RWC and RWCD, if you think about it).

    I'm reluctant to complicate the syntax further for something that is
    only occasionally needed.

  - we'd need either (a) code to parse the log files, or, (b) code to
    maintain "who created this ref" on every push that creates a ref.

      - parsing the log files is too kludgy and inelegant to be in core,
        not to mention potentially very slow for really large projects

      - code to maintain the a history of "who created this ref" is too
        cumbersome, especially because of the need to expire old entries
        after a time.
This commit is contained in:
Sitaram Chamarty 2011-05-01 09:06:53 +05:30
parent 59f3c4a512
commit 89b68bf5ca

84
contrib/adc/delete-branch Executable file
View file

@ -0,0 +1,84 @@
#!/usr/bin/perl
use strict;
use warnings;
# allow a user to delete a ref if the last create of the ref was done by the
# same user, *and* it was done within a certain time limie
# change this to suit your needs
my $oldest = 60*60*24*7; # in seconds
# use a generic error message to avoid information leak
my $error = "didn't find repo/ref, or the ref is too old, or you did not create it\n";
# ----
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
# arg check
die "need two arguments, a reponame and a refname\n" unless @ARGV == 2;
# get the repo name
my $repo = shift;
$repo =~ s/\.git$//;
# get the ref name to be deleted, and allow the same convenience shortcut
# (prefix "refs/heads/" if it doesn't start with "refs/") as in the main
# config file
my $ref = shift;
$ref =~ m(^refs/) or $ref =~ s(^)(refs/heads/);
# XXX WARNING: we do not do any access control checking -- we just go by the
# fact that if *you* created a branch within the last $limit seconds (default
# value is 1 week), you are allowed to delete the branch.
# find the earliest log entry we're willing to look at
my $limit = `date -d '$oldest seconds ago' '+%F.%T'`;
# NOTE: this is the format that gitolite uses in its log entries (see sub
# 'get_logfilename in one of the pm files). The logic also depends on the
# fact that this is sortable, because we read backwards and stop when we
# reach something older than $limit
chomp($limit);
# find the last 2 log files; here also we depend on the fact that the file
# *names* are time ordered when sorted
my ($lf1, $lf2) = reverse sort glob("$ENV{GL_ADMINDIR}/logs/gitolite*log");
my $found = 0;
my($ts, $user, $ip, $cmd, $op, $oldsha, $newsha, $logrepo, $logref, $refrule);
for my $lf ($lf1, $lf2) {
next unless $lf;
open(LF, "-|", "tac", $lf) or die "tac $lf failed: $!\n";
while (<LF>) {
($ts, $user, $ip, $cmd, $op, $oldsha, $newsha, $logrepo, $logref, $refrule) = split /\t/;
next unless $refrule;
if ($ts le $limit) {
# we don't look at entries earlier than this
$found = -1;
last;
}
if ($op eq 'C' and $oldsha =~ /^0+$/ and $logrepo eq $repo and $logref eq $ref) {
# creation record found; no need to look at any more entries
$found = 1;
last;
}
}
last if $found;
}
# check user in creation record to make sure it is the same one
if ($found == 1 and $user eq $ENV{GL_USER}) {
chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git") or die "chdir $ENV{GL_REPO_BASE_ABS}/$repo.git failed: $!\n";
system("git", "update-ref", "-d", $ref, $newsha) and die "ref deletion failed\n";
warn "deleted $ref from $repo (created on $ts)\n";
# NOTE: we use warn so this gets into the log in some way; perhaps
# later we can adjust the format to more closely resemble a normal
# remote delete operation
exit 0;
}
print STDERR $error;
exit 1;