Merge branch 'master' into autoconfig
[ikiwiki.git] / IkiWiki / Plugin / git.pm
similarity index 79%
rename from IkiWiki/Rcs/git.pm
rename to IkiWiki/Plugin/git.pm
index 1fa9188aa3c29fbfa35065382bb0c92ef5eb25b8..b683e4ec3b7fbba4b1f41c5bb7ac96c074ec2fd2 100644 (file)
@@ -1,6 +1,5 @@
 #!/usr/bin/perl
-
-package IkiWiki;
+package IkiWiki::Plugin::git;
 
 use warnings;
 use strict;
@@ -11,7 +10,86 @@ 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
 
-sub _safe_git (&@) { #{{{
+sub import { #{{{
+       if (exists $IkiWiki::hooks{rcs}) {
+               error(gettext("cannot use multiple rcs plugins"));
+       }
+       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 (defined $config{git_wrapper} && length $config{git_wrapper}) {
+               push @{$config{wrappers}}, {
+                       wrapper => $config{git_wrapper},
+                       wrappermode => (defined $config{git_wrappermode} ? $config{git_wrappermode} : "06755"),
+               };
+       }
+} #}}}
+
+sub getsetup () { #{{{
+       return
+               git_wrapper => {
+                       type => "string",
+                       example => "/git/wiki.git/hooks/post-update",
+                       description => "git post-update executable to generate",
+                       safe => 0, # file
+                       rebuild => 0,
+               },
+               git_wrappermode => {
+                       type => "string",
+                       example => '06755',
+                       description => "mode for git_wrapper (can safely be made suid)",
+                       safe => 0,
+                       rebuild => 0,
+               },
+               historyurl => {
+                       type => "string",
+                       example => "http://git.example.com/gitweb.cgi?p=wiki.git;a=history;f=[[file]]",
+                       description => "gitweb url to show file history ([[file]] substituted)",
+                       safe => 1,
+                       rebuild => 1,
+               },
+               diffurl => {
+                       type => "string",
+                       example => "http://git.example.com/gitweb.cgi?p=wiki.git;a=blobdiff;h=[[sha1_to]];hp=[[sha1_from]];hb=[[sha1_parent]];f=[[file]]",
+                       description => "gitweb url to show a diff ([[sha1_to]], [[sha1_from]], [[sha1_parent]], and [[file]] substituted)",
+                       safe => 1,
+                       rebuild => 1,
+               },
+               gitorigin_branch => {
+                       type => "string",
+                       example => "origin",
+                       description => "where to pull and push changes (set to empty string to disable)",
+                       safe => 0, # paranoia
+                       rebuild => 0,
+               },
+               gitmaster_branch => {
+                       type => "string",
+                       example => "master",
+                       description => "branch that the wiki is stored in",
+                       safe => 0, # paranoia
+                       rebuild => 0,
+               },
+} #}}}
+
+sub safe_git (&@) { #{{{
        # Start a child process safely without resorting /bin/sh.
        # Return command output or success state (in scalar context).
 
@@ -43,12 +121,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
@@ -142,7 +220,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.
 
@@ -262,7 +340,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;
        }
 
@@ -315,7 +393,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;
        }
 
@@ -336,7 +414,7 @@ sub rcs_commit_staged ($$$) {
                $ENV{GIT_AUTHOR_EMAIL}="$u\@web";
        }
 
-       $message = possibly_foolish_untaint($message);
+       $message = IkiWiki::possibly_foolish_untaint($message);
        my @opts;
        if ($message !~ /\S/) {
                # Force git to allow empty commit messages.
@@ -406,7 +484,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;