From e09888e2caa69ebbfce08d5511eaf1aca86c78eb Mon Sep 17 00:00:00 2001 From: "Kyle J. McKay" Date: Tue, 15 Jun 2021 09:59:01 -0700 Subject: [PATCH] init: optionally create an initial empty commit If the new setting $Girocco::Config::empty_commit_message is defined (it can be the empty string) then when creating a new push project, create an initial commit that has the empty tree and the given $Girocco::Config::empty_commit_message as its commit message. What this does is cause a remote client that's cloning the newly created push project to automatically set up the HEAD symbolic ref to match whatever the $initial_branch setting has been set to (as well as suppress the "You appear to have cloned an empty repository." message). It's no less convenient for the remote user since the commit has an empty tree. It may be a bit surprising to find an empty commit already present. That's why this setting is off by default. However, when it's important that the $initial_branch setting be respected by the remote client on a newly created push project, setting $Girocco::Config::empty_commit_message to any value will help make sure that happens. Signed-off-by: Kyle J. McKay --- Girocco/Config.pm | 21 ++++++++++++++++++ Girocco/Project.pm | 20 ++++++++++++++++- Girocco/Validator.pm | 4 ++++ bin/create-initial-empty-commit | 48 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 1 deletion(-) create mode 100755 bin/create-initial-empty-commit diff --git a/Girocco/Config.pm b/Girocco/Config.pm index 5ee3e52..3f4f937 100644 --- a/Girocco/Config.pm +++ b/Girocco/Config.pm @@ -336,11 +336,32 @@ our $mailsh_sizelimit = 512; # If this value is unset or invalid the default initial branch will always # be "master". Note that this only applies to newly created "push" projects; # mirror projects and "adopted" projects ignore this setting. +# Set the $empty_commit_message setting to make this setting truly take effect. # RECOMMENDED VALUE: whatever-your-favored-initial-branch-name-is #our $initial_branch = "supercalifragilisticexpialidocious"; #our $initial_branch = "frabjous"; our $initial_branch = undef; +# When creating a new push project, if this is DEFINED to any value (including +# the empty string), then an initial empty commit will be added to the newly +# created push project that has an empty tree and contains the $empty_commit_message +# as its commit message. By doing so, the initial branch will NOT be unborn and +# as a result, when cloning such a project the clone WILL respect the $initial_branch +# setting and set up its HEAD symbolic-ref to match. Since the initial commit will +# have an empty tree it should be no less convenient than cloning a project with an +# unborn initial branch. In fact, it should be more convenient as the expected +# $initial_branch will be checked out rather than whatever random initial branch the +# client might otherwise be inclined to set up for a newly initialized empty project. +# Defining this will also suppress the "You appear to have cloned an empty repository." +# message that would otherwise be generated by the Git client when cloning a newly +# created push project since the project will no longer technically be "empty". +# RECOMMENDED VALUE: defined if the $initial_branch setting should be respected +#our $empty_commit_message = "create project"; +#our $empty_commit_message = "initial empty commit"; +#our $empty_commit_message = "initial commit\n\ngit add ...\ngit commit\ngit push"; +#our $empty_commit_message = ""; +our $empty_commit_message = undef; + # ## ------------------- diff --git a/Girocco/Project.pm b/Girocco/Project.pm index 3ed7d04..29f27ef 100644 --- a/Girocco/Project.pm +++ b/Girocco/Project.pm @@ -1235,6 +1235,10 @@ sub conjure { defined($cur_head) && $cur_head =~ m{^refs/heads/\Q$def_head\E$} or $self->set_HEAD($def_head); $self->{HEAD} = $def_head; + if (defined($Girocco::Config::empty_commit_message)) { + system("$Girocco::Config::basedir/bin/create-initial-empty-commit", $self->{name}) == 0 + or die "create-initial-empty-commit $self->{name} failed"; + } 1; } @@ -1289,7 +1293,7 @@ sub adopt { or die "create-personal-mob-area $self->{name} failed"; } my $result = $self->perm_initialize; - unlink("$self->{path}/.delaygc") unless $self->is_empty; + unlink("$self->{path}/.delaygc") unless $self->is_empty(1); # Pick up any pre-existing settings my $p = Girocco::Project->load($name); %$self = %$p if defined($p) && $p->{loaded}; @@ -1519,8 +1523,14 @@ sub is_empty { # have any refs. This means packed-refs does not exist or is # empty or only has lines starting with '#' AND there are no # files in the refs subdirectory hierarchy (no matter how deep). + # As a "kludge" to accomodate the $Girocco::Config::empty_commit_message + # setting, if the packed-refs file does not exist or contains no + # refs, and the .delaygc file exists and the .allowgc file does not + # then the project will be considered empty unless it's a mirror. + # But if a true value is passed as the first argument, disable this kludge. my $self = shift; + my $nokludge = shift; (-d $self->{path}) or return 0; if (-e $self->{path}.'/packed-refs') { @@ -1536,6 +1546,14 @@ sub is_empty { return 0 if $foundref; } (-d $self->{path}.'/refs') or return 1; + # Begin kludge to accomodate $empty_commit_message setting + if (!$nokludge && !$self->{mirror} && -e $self->{path}.'/.delaygc' && ! -e $self->{path}.'/.allowgc') { + # Sure looks like an empty push project + # The non-kludge is to make sure there's only exactly one ref, HEAD is a + # symbolic-ref to that ref and the commit has no parents and an empty tree + # while still requiring the !mirror, .delaygc && !.allowgc conditions too. + return 1; + } return !_contains_files($self->{path}.'/refs'); } diff --git a/Girocco/Validator.pm b/Girocco/Validator.pm index 6cd7aac..f1a4fb2 100644 --- a/Girocco/Validator.pm +++ b/Girocco/Validator.pm @@ -76,6 +76,10 @@ defined($mailsh_sizelimit) && $mailsh_sizelimit =~ /^[1-9][0-9]*$/ or !defined($initial_branch) || $initial_branch eq "" || $initial_branch !~ /^\/|^\.|[\x00-\x1f \x7f\[\[~^"'"<>*?\\:]|\@\{|\.\.|\.lock$|\.$|\/$/ or die "Girocco::Config: \$initial_branch grossly invalid: $initial_branch"; +if (defined($empty_commit_message)) { + $empty_commit_message =~ s/^\s+//; + $empty_commit_message =~ s/\s+$//; +} $admincc = $admincc ? 1 : 0; $rootcert = "$certsdir/girocco_root_crt.pem" if $httpspushurl && !$rootcert; $clientcert = "$certsdir/girocco_client_crt.pem" if $httpspushurl && !$clientcert; diff --git a/bin/create-initial-empty-commit b/bin/create-initial-empty-commit new file mode 100755 index 0000000..3e1ed02 --- /dev/null +++ b/bin/create-initial-empty-commit @@ -0,0 +1,48 @@ +#!/bin/sh + +set -e + +. @basedir@/shlib.sh + +[ -n "$defined_cfg_empty_commit_message" ] || exit 0 # nothing to do, not enabled + +proj="$1" +[ -d "$cfg_reporoot/$proj.git" ] || { echo "Invalid project name: $1" >&2; exit 1; } + +[ -f "$cfg_reporoot/$proj.git/.nofetch" ] || { echo "Mirrors do not create an initial empty commit: $1" >&2; exit 1; } + +projdir="$cfg_reporoot/$proj.git" +if + [ -L "$projdir/HEAD" ] || + ! [ -f "$projdir/HEAD" ] || + ! [ -f "$projdir/config" ] || + ! [ -f "$projdir/description" ] || + ! [ -f "$projdir/hooks/pre-receive" ] || + ! [ -x "$projdir/hooks/pre-receive" ] || + ! [ -f "$projdir/hooks/post-receive" ] || + ! [ -x "$projdir/hooks/post-receive" ] || + ! [ -f "$projdir/hooks/update" ] || + ! [ -x "$projdir/hooks/update" ] || + ! [ -d "$projdir/objects" ] || + ! [ -d "$projdir/info" ] +then + echo "Incorrectly set up project: $1" >&2 + exit 1 +fi + +umask $var_umask_perm + +cd "$projdir" || { echo "Could not cd to project: $proj" >&2; exit 1; } +if symref="$(git symbolic-ref -q HEAD)" && ! git show-ref --verify -q "$symref"; then + # only do anything if HEAD is an unborn symbolic ref + mttree="$(git mktree