X-Git-Url: https://sipb.mit.edu/gitweb.cgi/ikiwiki.git/blobdiff_plain/38bf2f6388d506e741395992617a900843e9ba0d..237ea79d715fbba5c1be0b73fbe008b3221975fb:/IkiWiki/Plugin/git.pm diff --git a/IkiWiki/Plugin/git.pm b/IkiWiki/Plugin/git.pm index 992c6226b..3ccaa446a 100644 --- a/IkiWiki/Plugin/git.pm +++ b/IkiWiki/Plugin/git.pm @@ -27,6 +27,8 @@ sub import { hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime); hook(type => "rcs", id => "rcs_getmtime", call => \&rcs_getmtime); hook(type => "rcs", id => "rcs_receive", call => \&rcs_receive); + hook(type => "rcs", id => "rcs_preprevert", call => \&rcs_preprevert); + hook(type => "rcs", id => "rcs_revert", call => \&rcs_revert); } sub checkconfig () { @@ -41,11 +43,14 @@ sub checkconfig () { push @{$config{wrappers}}, { wrapper => $config{git_wrapper}, wrappermode => (defined $config{git_wrappermode} ? $config{git_wrappermode} : "06755"), + wrapper_background_command => $config{git_wrapper_background_command}, }; } if (defined $config{git_test_receive_wrapper} && - length $config{git_test_receive_wrapper}) { + length $config{git_test_receive_wrapper} && + defined $config{untrusted_committers} && + @{$config{untrusted_committers}}) { push @{$config{wrappers}}, { test_receive => 1, wrapper => $config{git_test_receive_wrapper}, @@ -78,6 +83,13 @@ sub getsetup () { safe => 0, # file rebuild => 0, }, + git_wrapper_background_command => { + type => "string", + example => "git push github", + description => "shell command for git_wrapper to run, in the background", + safe => 0, # command + rebuild => 0, + }, git_wrappermode => { type => "string", example => '06755', @@ -101,7 +113,7 @@ sub getsetup () { }, historyurl => { type => "string", - example => "http://git.example.com/gitweb.cgi?p=wiki.git;a=history;f=[[file]]", + example => "http://git.example.com/gitweb.cgi?p=wiki.git;a=history;f=[[file]];hb=HEAD", description => "gitweb url to show file history ([[file]] substituted)", safe => 1, rebuild => 1, @@ -452,7 +464,7 @@ sub rcs_update () { # Update working directory. if (length $config{gitorigin_branch}) { - run_or_cry('git', 'pull', $config{gitorigin_branch}); + run_or_cry('git', 'pull', '--prune', $config{gitorigin_branch}); } } @@ -509,6 +521,8 @@ sub rcs_commit_staged (@) { } if (defined $params{session}->param("nickname")) { $u=encode_utf8($params{session}->param("nickname")); + $u=~s/\s+/_/g; + $u=~s/[^-_0-9[:alnum:]]+//g; } if (defined $u) { $ENV{GIT_AUTHOR_EMAIL}="$u\@web"; @@ -706,10 +720,16 @@ sub rcs_getmtime ($) { return findtimes($file, 0); } -sub rcs_receive () { +{ +my $git_root; + +sub git_find_root { # The wiki may not be the only thing in the git repo. # Determine if it is in a subdirectory by examining the srcdir, # and its parents, looking for the .git directory. + + return $git_root if defined $git_root; + my $subdir=""; my $dir=$config{srcdir}; while (! -d "$dir/.git") { @@ -720,83 +740,128 @@ sub rcs_receive () { } } + return $git_root=$subdir; +} + +} + +sub git_parse_changes { + my @changes = @_; + + my $subdir = git_find_root(); + my @rets; + foreach my $ci (@changes) { + foreach my $detail (@{ $ci->{'details'} }) { + my $file = $detail->{'file'}; + + # check that all changed files are in the subdir + if (length $subdir && + ! ($file =~ s/^\Q$subdir\E//)) { + error sprintf(gettext("you are not allowed to change %s"), $file); + } + + my ($action, $mode, $path); + if ($detail->{'status'} =~ /^[M]+\d*$/) { + $action="change"; + $mode=$detail->{'mode_to'}; + } + elsif ($detail->{'status'} =~ /^[AM]+\d*$/) { + $action="add"; + $mode=$detail->{'mode_to'}; + } + elsif ($detail->{'status'} =~ /^[DAM]+\d*/) { + $action="remove"; + $mode=$detail->{'mode_from'}; + } + else { + error "unknown status ".$detail->{'status'}; + } + + # test that the file mode is ok + if ($mode !~ /^100[64][64][64]$/) { + error sprintf(gettext("you cannot act on a file with mode %s"), $mode); + } + if ($action eq "change") { + if ($detail->{'mode_from'} ne $detail->{'mode_to'}) { + error gettext("you are not allowed to change file modes"); + } + } + + # extract attachment to temp file + if (($action eq 'add' || $action eq 'change') && + ! pagetype($file)) { + eval q{use File::Temp}; + die $@ if $@; + my $fh; + ($fh, $path)=File::Temp::tempfile("XXXXXXXXXX", UNLINK => 1); + # Ensure we run this in the right place, + # see comments in rcs_receive. + my $cmd = ($no_chdir ? '' : "cd $config{srcdir} && ") + . "git show $detail->{sha1_to} > '$path'"; + if (system($cmd) != 0) { + error("failed writing temp file '$path'."); + } + } + + push @rets, { + file => $file, + action => $action, + path => $path, + }; + } + } + + return @rets; +} + +sub rcs_receive () { my @rets; while (<>) { chomp; my ($oldrev, $newrev, $refname) = split(' ', $_, 3); - + # only allow changes to gitmaster_branch if ($refname !~ /^refs\/heads\/\Q$config{gitmaster_branch}\E$/) { error sprintf(gettext("you are not allowed to change %s"), $refname); } - + # Avoid chdir when running git here, because the changes # are in the master git repo, not the srcdir repo. - # The pre-recieve hook already puts us in the right place. + # The pre-receive hook already puts us in the right place. $no_chdir=1; - my @changes=git_commit_info($oldrev."..".$newrev); + push @rets, git_parse_changes(git_commit_info($oldrev."..".$newrev)); $no_chdir=0; + } - foreach my $ci (@changes) { - foreach my $detail (@{ $ci->{'details'} }) { - my $file = $detail->{'file'}; + return reverse @rets; +} - # check that all changed files are in the - # subdir - if (length $subdir && - ! ($file =~ s/^\Q$subdir\E//)) { - error sprintf(gettext("you are not allowed to change %s"), $file); - } +sub rcs_preprevert (@) { + my %params = @_; + my $rev = $params{rev}; - my ($action, $mode, $path); - if ($detail->{'status'} =~ /^[M]+\d*$/) { - $action="change"; - $mode=$detail->{'mode_to'}; - } - elsif ($detail->{'status'} =~ /^[AM]+\d*$/) { - $action="add"; - $mode=$detail->{'mode_to'}; - } - elsif ($detail->{'status'} =~ /^[DAM]+\d*/) { - $action="remove"; - $mode=$detail->{'mode_from'}; - } - else { - error "unknown status ".$detail->{'status'}; - } - - # test that the file mode is ok - if ($mode !~ /^100[64][64][64]$/) { - error sprintf(gettext("you cannot act on a file with mode %s"), $mode); - } - if ($action eq "change") { - if ($detail->{'mode_from'} ne $detail->{'mode_to'}) { - error gettext("you are not allowed to change file modes"); - } - } - - # extract attachment to temp file - if (($action eq 'add' || $action eq 'change') && - ! pagetype($file)) { - eval q{use File::Temp}; - die $@ if $@; - my $fh; - ($fh, $path)=File::Temp::tempfile("XXXXXXXXXX", UNLINK => 1); - if (system("git show ".$detail->{sha1_to}." > '$path'") != 0) { - error("failed writing temp file"); - } - } + # Note test_changes expects 'cgi' and 'session' parameters. + require IkiWiki::Receive; + IkiWiki::Receive::test_changes(%params, changes => + [git_parse_changes(git_commit_info($rev, 1))]); +} - push @rets, { - file => $file, - action => $action, - path => $path, - }; - } - } - } +sub rcs_revert (@) { + # Try to revert the given patch; returns undef on _success_. + my %params = @_; + my $rev = $params{rev}; - return reverse @rets; + if (run_or_non('git', 'revert', '--no-commit', $rev)) { + debug "Committing revert for patch '$rev'."; + rcs_commit_staged(message => + sprintf(gettext("This reverts commit %s"), $rev), @_); + } + else { + # No idea what is actually getting reverted, so all we can + # do is say we failed. + run_or_die('git', 'reset', '--hard'); + return sprintf(gettext("Failed to revert commit %s"), $rev); + } } 1