From 89b68bf5ca99508caaa768c60ce910d7e0a29ccf Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sun, 1 May 2011 09:06:53 +0530 Subject: [PATCH] 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. --- contrib/adc/delete-branch | 84 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100755 contrib/adc/delete-branch diff --git a/contrib/adc/delete-branch b/contrib/adc/delete-branch new file mode 100755 index 000000000..fdc7b6c20 --- /dev/null +++ b/contrib/adc/delete-branch @@ -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 () { + ($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;