X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/14cd75746a2c73b50548b6fdb3583d536b6ef9bd..78e4d5bc6f1a55b400611d12185369b6e76c0f33:/IkiWiki/Plugin/git.pm?ds=sidebyside diff --git a/IkiWiki/Plugin/git.pm b/IkiWiki/Plugin/git.pm index 6c9aca650..14b0ab285 100644 --- a/IkiWiki/Plugin/git.pm +++ b/IkiWiki/Plugin/git.pm @@ -1,6 +1,5 @@ #!/usr/bin/perl - -package IkiWiki; +package IkiWiki::Plugin::git; use warnings; use strict; @@ -11,30 +10,46 @@ use open qw{:utf8 :std}; my $sha1_pattern = qr/[0-9a-fA-F]{40}/; # pattern to validate Git sha1sums my $dummy_commit_msg = 'dummy commit'; # message to skip in recent changes -hook(type => "checkconfig", id => "git", call => sub { #{{{ - if (! defined $config{diffurl}) { - $config{diffurl}=""; - } +sub import { #{{{ + hook(type => "checkconfig", id => "git", call => \&checkconfig); + hook(type => "getsetup", id => "git", call => \&getsetup); + hook(type => "rcs", id => "rcs_update", call => \&rcs_update); + hook(type => "rcs", id => "rcs_prepedit", call => \&rcs_prepedit); + hook(type => "rcs", id => "rcs_commit", call => \&rcs_commit); + hook(type => "rcs", id => "rcs_commit_staged", call => \&rcs_commit_staged); + hook(type => "rcs", id => "rcs_add", call => \&rcs_add); + hook(type => "rcs", id => "rcs_remove", call => \&rcs_remove); + hook(type => "rcs", id => "rcs_rename", call => \&rcs_rename); + hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges); + hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff); + hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime); +} #}}} + +sub checkconfig () { #{{{ if (! defined $config{gitorigin_branch}) { $config{gitorigin_branch}="origin"; } if (! defined $config{gitmaster_branch}) { $config{gitmaster_branch}="master"; } - if (length $config{git_wrapper}) { + if (defined $config{git_wrapper} && length $config{git_wrapper}) { push @{$config{wrappers}}, { wrapper => $config{git_wrapper}, wrappermode => (defined $config{git_wrappermode} ? $config{git_wrappermode} : "06755"), }; } -}); #}}} +} #}}} -hook(type => "getsetup", id => "git", call => sub { #{{{ +sub getsetup () { #{{{ return + plugin => { + safe => 0, # rcs plugin + rebuild => undef, + }, git_wrapper => { type => "string", example => "/git/wiki.git/hooks/post-update", - description => "git post-update executable to generate", + description => "git hook to generate", safe => 0, # file rebuild => 0, }, @@ -73,9 +88,9 @@ hook(type => "getsetup", id => "git", call => sub { #{{{ safe => 0, # paranoia rebuild => 0, }, -}); #}}} +} #}}} -sub _safe_git (&@) { #{{{ +sub safe_git (&@) { #{{{ # Start a child process safely without resorting /bin/sh. # Return command output or success state (in scalar context). @@ -107,12 +122,12 @@ sub _safe_git (&@) { #{{{ return wantarray ? @lines : ($? == 0); } # Convenient wrappers. -sub run_or_die ($@) { _safe_git(\&error, @_) } -sub run_or_cry ($@) { _safe_git(sub { warn @_ }, @_) } -sub run_or_non ($@) { _safe_git(undef, @_) } +sub run_or_die ($@) { safe_git(\&error, @_) } +sub run_or_cry ($@) { safe_git(sub { warn @_ }, @_) } +sub run_or_non ($@) { safe_git(undef, @_) } #}}} -sub _merge_past ($$$) { #{{{ +sub merge_past ($$$) { #{{{ # Unlike with Subversion, Git cannot make a 'svn merge -rN:M file'. # Git merge commands work with the committed changes, except in the # implicit case of '-m' of git checkout(1). So we should invent a @@ -206,7 +221,7 @@ sub _merge_past ($$$) { #{{{ return $conflict; } #}}} -sub _parse_diff_tree ($@) { #{{{ +sub parse_diff_tree ($@) { #{{{ # Parse the raw diff tree chunk and return the info hash. # See git-diff-tree(1) for the syntax. @@ -293,13 +308,16 @@ sub _parse_diff_tree ($@) { #{{{ my $sha1_to = shift(@tmp); my $status = shift(@tmp); + # git does not output utf-8 filenames, but instead + # double-quotes them with the utf-8 characters + # escaped as \nnn\nnn. if ($file =~ m/^"(.*)"$/) { ($file=$1) =~ s/\\([0-7]{1,3})/chr(oct($1))/eg; } $file =~ s/^\Q$prefix\E//; if (length $file) { push @{ $ci{'details'} }, { - 'file' => decode_utf8($file), + 'file' => decode("utf8", $file), 'sha1_from' => $sha1_from[0], 'sha1_to' => $sha1_to, }; @@ -326,7 +344,7 @@ sub git_commit_info ($;$) { #{{{ my ($prefix) = run_or_die('git', 'rev-parse', '--show-prefix'); my @ci; - while (my $parsed = _parse_diff_tree(($prefix or ""), \@raw_lines)) { + while (my $parsed = parse_diff_tree(($prefix or ""), \@raw_lines)) { push @ci, $parsed; } @@ -379,7 +397,7 @@ sub rcs_commit ($$$;$$) { #{{{ my ($prev) = $rcstoken =~ /^($sha1_pattern)$/; # untaint if (defined $cur && defined $prev && $cur ne $prev) { - my $conflict = _merge_past($prev, $file, $dummy_commit_msg); + my $conflict = merge_past($prev, $file, $dummy_commit_msg); return $conflict if defined $conflict; } @@ -400,11 +418,23 @@ sub rcs_commit_staged ($$$) { $ENV{GIT_AUTHOR_EMAIL}="$u\@web"; } + $message = IkiWiki::possibly_foolish_untaint($message); + my @opts; + if ($message !~ /\S/) { + # Force git to allow empty commit messages. + # (If this version of git supports it.) + my ($version)=`git --version` =~ /git version (.*)/; + if ($version ge "1.5.4") { + push @opts, '--cleanup=verbatim'; + } + else { + $message.="."; + } + } + push @opts, '-q'; # git commit returns non-zero if file has not been really changed. # so we should ignore its exit status (hence run_or_non). - $message = possibly_foolish_untaint($message); - if (run_or_non('git', 'commit', '--cleanup=verbatim', - '-q', '-m', $message)) { + if (run_or_non('git', 'commit', @opts, '-m', $message)) { if (length $config{gitorigin_branch}) { run_or_cry('git', 'push', $config{gitorigin_branch}); } @@ -458,7 +488,7 @@ sub rcs_recentchanges ($) { #{{{ foreach my $detail (@{ $ci->{'details'} }) { my $file = $detail->{'file'}; - my $diffurl = $config{'diffurl'}; + my $diffurl = defined $config{'diffurl'} ? $config{'diffurl'} : ""; $diffurl =~ s/\[\[file\]\]/$file/go; $diffurl =~ s/\[\[sha1_parent\]\]/$ci->{'parent'}/go; $diffurl =~ s/\[\[sha1_from\]\]/$detail->{'sha1_from'}/go;