X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/afedd60d7911b35aa3fb86dad24d61809946b9ed..24cd0fdb428195042afb4a289aeadfa1ec4e36c4:/IkiWiki/Rcs/git.pm?ds=sidebyside

diff --git a/IkiWiki/Rcs/git.pm b/IkiWiki/Rcs/git.pm
index 271c27af6..7f33148e3 100644
--- a/IkiWiki/Rcs/git.pm
+++ b/IkiWiki/Rcs/git.pm
@@ -8,11 +8,8 @@ use open qw{:utf8 :std};
 
 package IkiWiki;
 
-my $origin_branch    = 'origin';            # Git ref for main repository
-my $master_branch    = 'master';            # working branch
 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
-my $web_commit_msg   = qr/^web commit (by (\w+)|from (\d+\.\d+\.\d+\.\d+)):?(.*)/;
 
 sub _safe_git (&@) { #{{{
 	# Start a child process safely without resorting /bin/sh.
@@ -48,7 +45,7 @@ sub _safe_git (&@) { #{{{
 	return wantarray ? @lines : ($? == 0);
 }
 # Convenient wrappers.
-sub run_or_die ($@) { _safe_git(\&IkiWiki::error, @_) }
+sub run_or_die ($@) { _safe_git(\&error, @_) }
 sub run_or_cry ($@) { _safe_git(sub { warn @_ },  @_) }
 sub run_or_non ($@) { _safe_git(undef,            @_) }
 #}}}
@@ -113,8 +110,8 @@ sub _merge_past ($$$) { #{{{
 
 		# Switch to throw-away branch for the merge operation.
 		push @undo, sub {
-			if (!run_or_cry('git-checkout', $master_branch)) {
-				run_or_cry('git-checkout','-f',$master_branch);
+			if (!run_or_cry('git-checkout', $config{gitmaster_branch})) {
+				run_or_cry('git-checkout','-f',$config{gitmaster_branch});
 			}
 		};
 		run_or_die('git-checkout', $branch);
@@ -126,7 +123,7 @@ sub _merge_past ($$$) { #{{{
 		# _Silently_ commit all modifications in the current branch.
 		run_or_non('git-commit', '-m', $message, '-a');
 		# ... and re-switch to master.
-		run_or_die('git-checkout', $master_branch);
+		run_or_die('git-checkout', $config{gitmaster_branch});
 
 		# Attempt to merge without complaining.
 		if (!run_or_non('git-pull', '--no-commit', '.', $branch)) {
@@ -154,14 +151,15 @@ sub _parse_diff_tree (@) { #{{{
 	my ($dt_ref) = @_;
 
 	# End of stream?
-	return if !defined @{ $dt_ref } || !length @{ $dt_ref }[0];
+	return if !defined @{ $dt_ref } ||
+		  !defined @{ $dt_ref }[0] || !length @{ $dt_ref }[0];
 
 	my %ci;
 	# Header line.
 	HEADER: while (my $line = shift @{ $dt_ref }) {
 		return if $line !~ m/^(.+) ($sha1_pattern)/;
 
-		my $sha1 = $1;
+		my $sha1 = $2;
 		$ci{'sha1'} = $sha1;
 		last HEADER;
 	}
@@ -171,10 +169,12 @@ sub _parse_diff_tree (@) { #{{{
 		# Regexps are semi-stolen from gitweb.cgi.
 		if ($line =~ m/^tree ([0-9a-fA-F]{40})$/) {
 			$ci{'tree'} = $1;
-		} elsif ($line =~ m/^parent ([0-9a-fA-F]{40})$/) {
+		}
+		elsif ($line =~ m/^parent ([0-9a-fA-F]{40})$/) {
 			# XXX: collecting in reverse order
 			push @{ $ci{'parents'} }, $1;
-		} elsif ($line =~ m/^(author|committer) (.*) ([0-9]+) (.*)$/) {
+		}
+		elsif ($line =~ m/^(author|committer) (.*) ([0-9]+) (.*)$/) {
 			my ($who, $name, $epoch, $tz) =
 			   ($1,   $2,    $3,     $4 );
 
@@ -186,20 +186,22 @@ sub _parse_diff_tree (@) { #{{{
 				my ($fullname, $username) = ($1, $2);
 				$ci{"${who}_fullname"}    = $fullname;
 				$ci{"${who}_username"}    = $username;
-			} else {
+			}
+			else {
 				$ci{"${who}_fullname"} =
 					$ci{"${who}_username"} = $name;
 			}
-		} elsif ($line =~ m/^$/) {
+		}
+		elsif ($line =~ m/^$/) {
 			# Trailing empty line signals next section.
 			last IDENT;
 		}
 	}
 
-	error("No 'tree' or 'parents' seen in diff-tree output")
+	debug("No 'tree' or 'parents' seen in diff-tree output")
 	    if !defined $ci{'tree'} || !defined $ci{'parents'};
 
-	$ci{'parent'} = @{ $ci{'parents'} }[0];
+	$ci{'parent'} = @{ $ci{'parents'} }[0] if defined $ci{'parents'};
 
 	# Commit message.
 	COMMENT: while (my $line = shift @{ $dt_ref }) {
@@ -240,14 +242,14 @@ sub _parse_diff_tree (@) { #{{{
 		last FILE;
 	}
 
-	warn "No detail in diff-tree output" if !defined $ci{'details'};
+	debug("No detail in diff-tree output") if !defined $ci{'details'};
 
 	return \%ci;
 } #}}}
 
-sub git_commit_info (;$$) { #{{{
+sub git_commit_info ($;$) { #{{{
 	# Return an array of commit info hashes of num commits (default: 1)
-	# starting from the given sha1sum (default: HEAD).
+	# starting from the given sha1sum.
 
 	my ($sha1, $num) = @_;
 
@@ -255,7 +257,7 @@ sub git_commit_info (;$$) { #{{{
 
 	my @raw_lines =
 	    run_or_die(qq{git-rev-list --max-count=$num $sha1 |
-		          git-diff-tree --stdin --pretty=raw -M -r});
+		          git-diff-tree --stdin --pretty=raw --always -M -m -r});
 
 	my @ci;
 	while (my $parsed = _parse_diff_tree(\@raw_lines)) {
@@ -283,7 +285,7 @@ sub git_sha1 (;$) { #{{{
 sub rcs_update () { #{{{
 	# Update working directory.
 
-	run_or_cry('git-pull', $origin_branch);
+	run_or_cry('git-pull', $config{gitorigin_branch});
 } #}}}
 
 sub rcs_prepedit ($) { #{{{
@@ -295,12 +297,21 @@ sub rcs_prepedit ($) { #{{{
 	return git_sha1($file);
 } #}}}
 
-sub rcs_commit ($$$) { #{{{
+sub rcs_commit ($$$;$$) { #{{{
 	# Try to commit the page; returns undef on _success_ and
 	# a version of the page with the rcs's conflict markers on
 	# failure.
 
-	my ($file, $message, $rcstoken) = @_;
+	my ($file, $message, $rcstoken, $user, $ipaddr) = @_;
+
+	if (defined $user) {
+		$message = "web commit by $user" .
+		    (length $message ? ": $message" : "");
+	}
+	elsif (defined $ipaddr) {
+		$message = "web commit from $ipaddr" .
+		    (length $message ? ": $message" : "");
+	}
 
 	# XXX: Wiki directory is in the unlocked state when starting this
 	# action.  But it takes time for a Git process to finish its job
@@ -324,7 +335,7 @@ sub rcs_commit ($$$) { #{{{
 	$message = possibly_foolish_untaint($message);
 	if (run_or_non('git-commit', '-m', $message, '-i', $file)) {
 		unlockwiki();
-		run_or_cry('git-push', $origin_branch);
+		run_or_cry('git-push', $config{gitorigin_branch});
 	}
 
 	return undef; # success
@@ -343,58 +354,60 @@ sub rcs_recentchanges ($) { #{{{
 
 	my ($num) = @_;
 
-	eval q{use CGI 'escapeHTML'};
 	eval q{use Date::Parse};
-	eval q{use Time::Duration};
+	error($@) if $@;
 
-	my ($sha1, $type, $when, $diffurl, $user, @pages, @message, @rets);
+	my @rets;
 	INFO: foreach my $ci (git_commit_info('HEAD', $num)) {
 		my $title = @{ $ci->{'comment'} }[0];
 
 		# Skip redundant commits.
 		next INFO if ($title eq $dummy_commit_msg);
 
-		$sha1 = $ci->{'sha1'};
-		$type = "web";
-		$when = concise(ago(time - $ci->{'author_epoch'}));
+		my ($sha1, $when) = (
+			$ci->{'sha1'},
+			time - $ci->{'author_epoch'}
+		);
 
-		foreach my $bit (@{ $ci->{'details'} }) {
+		my (@pages, @messages);
+		DETAIL: foreach my $detail (@{ $ci->{'details'} }) {
 			my $diffurl = $config{'diffurl'};
-			my $file    = $bit->{'file'};
+			my $file    = $detail->{'file'};
 
 			$diffurl =~ s/\[\[file\]\]/$file/go;
 			$diffurl =~ s/\[\[sha1_parent\]\]/$ci->{'parent'}/go;
-			$diffurl =~ s/\[\[sha1_from\]\]/$bit->{'sha1_from'}/go;
-			$diffurl =~ s/\[\[sha1_to\]\]/$bit->{'sha1_to'}/go;
+			$diffurl =~ s/\[\[sha1_from\]\]/$detail->{'sha1_from'}/go;
+			$diffurl =~ s/\[\[sha1_to\]\]/$detail->{'sha1_to'}/go;
 
 			push @pages, {
-				link => htmllink("", "", pagename($file), 1),
+				page => pagename($file),
 				diffurl => $diffurl,
-			},
+			};
 		}
+		push @messages, { line => $title };
 
-		push @message, { line => escapeHTML($title) };
+		my ($user, $type) = (q{}, "web");
 
-		if (defined $message[0] &&
-		    $message[0]->{line} =~ m/$web_commit_msg/) {
-			$user=defined $2 ? "$2" : "$3";
-			$message[0]->{line}=$4;
-		} else {
+		if (defined $messages[0] &&
+		    $messages[0]->{line} =~ m/$config{web_commit_regexp}/) {
+			$user = defined $2 ? "$2" : "$3";
+			$messages[0]->{line} = $4;
+		}
+		else {
 			$type ="git";
 			$user = $ci->{'author_username'};
 		}
 
 		push @rets, {
 			rev        => $sha1,
-			user       => htmllink("", "", $user, 1),
+			user       => $user,
 			committype => $type,
 			when       => $when,
-			message    => [@message],
+			message    => [@messages],
 			pages      => [@pages],
-		} if @pages;
+		};
 
-		$sha1 = $type = $when = $diffurl = $user = undef;
-		@pages = @message = ();
+		last INFO if @rets >= $num;
 	}
 
 	return @rets;
@@ -425,49 +438,24 @@ sub rcs_notify () { #{{{
 	my @changed_pages = map { $_->{'file'} } @{ $ci->{'details'} };
 
 	my ($user, $message);
-	if (@{ $ci->{'comment'} }[0] =~ m/$web_commit_msg/) {
+	if (@{ $ci->{'comment'} }[0] =~ m/$config{web_commit_regexp}/) {
 		$user    = defined $2 ? "$2" : "$3";
 		$message = $4;
-	} else {
+	}
+	else {
 		$user    = $ci->{'author_username'};
 		$message = join "\n", @{ $ci->{'comment'} };
 	}
 
 	require IkiWiki::UserInfo;
-	my @email_recipients = commit_notify_list($user, @changed_pages);
-	return if !@email_recipients;
-
-	# TODO: if a commit spans multiple pages, this will send
-	# subscribers a diff that might contain pages they did not
-	# sign up for. Should separate the diff per page and
-	# reassemble into one mail with just the pages subscribed to.
-	my $diff = join "\n", run_or_die('git-diff', "${sha1}^", $sha1);
-
-	my $subject = "$config{wikiname} update of ";
-	if (@changed_pages > 2) {
-		$subject .= "$changed_pages[0] $changed_pages[1] etc";
-	} else {
-		$subject .= join " ", @changed_pages;
-	}
-	$subject .= " by $user";
-
-	my $template = template("notifymail.tmpl");
-	$template->param(
-		wikiname => $config{wikiname},
-		diff     => $diff,
-		user     => $user,
-		message  => $message,
+	send_commit_mails(
+		sub {
+			$message;
+		},
+		sub {
+			join "\n", run_or_die('git-diff', "${sha1}^", $sha1);
+		}, $user, @changed_pages
 	);
-
-	eval q{use Mail::Sendmail};
-	foreach my $email (@email_recipients) {
-		sendmail(
-			To      => $email,
-			From    => "$config{wikiname} <$config{adminemail}>",
-			Subject => $subject,
-			Message => $template->output,
-		) or error("Failed to send update notification mail: $!");
-	}
 } #}}}
 
 sub rcs_getctime ($) { #{{{