diff options
author | Sitaram Chamarty <sitaram@atc.tcs.com> | 2011-05-01 09:06:53 +0530 |
---|---|---|
committer | Sitaram Chamarty <sitaram@atc.tcs.com> | 2011-05-02 07:15:12 +0530 |
commit | 89b68bf5ca99508caaa768c60ce910d7e0a29ccf (patch) | |
tree | 0b7ced3bbdd93174322ae0d2e52e105a673610d8 | |
parent | v2.0.1 (diff) | |
download | gitolite-gentoo-89b68bf5ca99508caaa768c60ce910d7e0a29ccf.tar.gz gitolite-gentoo-89b68bf5ca99508caaa768c60ce910d7e0a29ccf.tar.bz2 gitolite-gentoo-89b68bf5ca99508caaa768c60ce910d7e0a29ccf.zip |
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.
-rwxr-xr-x | contrib/adc/delete-branch | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/contrib/adc/delete-branch b/contrib/adc/delete-branch new file mode 100755 index 0000000..fdc7b6c --- /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 (<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; |