From d9563ea5516e8e786debf223e10ec11695aee9d7 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 9 Feb 2017 01:37:03 +0000 Subject: repobrowse: shorten internal names We'll still be keeping "repobrowse" for the public API for use with .psgi files, but shortening the name means less typing and we may have command-line tools, too. --- lib/PublicInbox/RepoGitLog.pm | 152 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 lib/PublicInbox/RepoGitLog.pm (limited to 'lib/PublicInbox/RepoGitLog.pm') diff --git a/lib/PublicInbox/RepoGitLog.pm b/lib/PublicInbox/RepoGitLog.pm new file mode 100644 index 00000000..9cfa526e --- /dev/null +++ b/lib/PublicInbox/RepoGitLog.pm @@ -0,0 +1,152 @@ +# Copyright (C) 2015 all contributors +# License: AGPL-3.0+ + +# show the log view +package PublicInbox::RepoGitLog; +use strict; +use warnings; +use PublicInbox::Hval qw(utf8_html); +use base qw(PublicInbox::RepoBase); +use PublicInbox::RepoGit qw(git_dec_links git_commit_title); +use PublicInbox::Qspawn; +# cannot rely on --date=format-local:... yet, it is too new (September 2015) +use constant STATES => qw(h p D ai an s b); +use constant STATE_BODY => (scalar(STATES) - 1); +my $LOG_FMT = '--pretty=tformat:'. join('%n', map { "%$_" } STATES).'%x00'; + +sub parent_links { + if (@_ == 1) { # typical, single-parent commit + qq( / parent $_[0]); + } elsif (@_ > 0) { # merge commit + ' / parents ' . + join(' ', map { qq($_) } @_); + } else { + ''; # root commit + } +} + +sub flush_log_hdr ($$$) { + my ($req, $dst, $hdr) = @_; + my $rel = $req->{relcmd}; + my $seen = $req->{seen}; + $$dst .= '
' if scalar keys %$seen;
+	my $id = $hdr->{h};
+	$seen->{$id} = 1;
+	$$dst .= qq();
+	$$dst .= utf8_html($hdr->{'s'}); # FIXME may still OOM
+	$$dst .= '';
+	my $D = $hdr->{D}; # FIXME: thousands of decorations may OOM us
+	if ($D ne '') {
+		$$dst .= ' (' . join(', ', git_dec_links($rel, $D)) . ')';
+	}
+	my @p = split(/ /, $hdr->{p});
+	push @{$req->{parents}}, @p;
+	my $plinks = parent_links(@p);
+	$$dst .= "\n- ";
+	$$dst .= utf8_html($hdr->{an});
+	$$dst .= " @ $hdr->{ai}\n  commit $id$plinks\n";
+	undef
+}
+
+sub git_log_sed_end ($$) {
+	my ($req, $dst) = @_;
+	$$dst .= '
';
+	my $m = '';
+	my $np = 0;
+	my $seen = $req->{seen};
+	my $git = $req->{repo_info}->{git};
+	my $rel = $req->{relcmd};
+	foreach my $p (@{$req->{parents}}) {
+		next if $seen->{$p};
+		$seen->{$p} = ++$np;
+		my $s = git_commit_title($git, $p);
+		$m .= qq(\n$p\t);
+		$s = defined($s) ? utf8_html($s) : '';
+		$m .= qq($s);
+	}
+	if ($np == 0) {
+		$$dst .= "No commits follow";
+	} elsif ($np > 1) {
+		$$dst .= "Unseen parent commits to follow (multiple choice):\n";
+	} else {
+		$$dst .= "Next parent to follow:\n";
+	}
+	$$dst .= $m;
+	$$dst .= '
'; +} + +sub git_log_sed ($$) { + my ($self, $req) = @_; + my $buf = ''; + my $state = 0; + $req->{seen} = {}; + $req->{parents} = []; + my $hdr = {}; + sub { + my $dst; + # $_[0] == scalar buffer, undef means EOF from "git log" + $dst = delete $req->{lhtml} || ''; + my @tmp; + if (defined $_[0]) { + $buf .= $_[0]; + @tmp = split(/\n/, $buf, -1); + $buf = @tmp ? pop(@tmp) : ''; + } else { + @tmp = split(/\n/, $buf, -1); + $buf = undef; + } + + foreach my $l (@tmp) { + if ($state != STATE_BODY) { + $hdr->{((STATES)[$state])} = $l; + if (++$state == STATE_BODY) { + flush_log_hdr($req, \$dst, $hdr); + $hdr = {}; + } + next; + } + if ($l eq "\0") { + $dst .= qq(
); + $state = 0; + } else { + $dst .= "\n"; + $dst .= utf8_html($l); + } + } + git_log_sed_end($req, \$dst) unless defined $buf; + $dst; + }; +} + +sub call_git_log { + my ($self, $req) = @_; + my $repo_info = $req->{repo_info}; + my $max = $repo_info->{max_commit_count} || 50; + $max = int($max); + $max = 50 if $max == 0; + my $env = $req->{env}; + my $q = $req->{'q'} = PublicInbox::RepoGitQuery->new($env); + my $h = $q->{h}; + $h eq '' and $h = 'HEAD'; + my $git = $repo_info->{git}; + my $cmd = $git->cmd(qw(log --no-notes --no-color --abbrev-commit), + $git->abbrev, $LOG_FMT, "-$max", $h, '--'); + my $rdr = { 2 => $git->err_begin }; + my $title = "log: $repo_info->{repo} (" . utf8_html($h). ')'; + $req->{lhtml} = $self->html_start($req, $title) . "\n\n"; + my $qsp = PublicInbox::Qspawn->new($cmd, undef, $rdr); + $qsp->psgi_return($env, undef, sub { + my ($r) = @_; + if (!defined $r) { + [ 500, [ 'Content-Type', 'text/html' ], [ $git->err ] ]; + } elsif ($r == 0) { + [ 404, [ 'Content-Type', 'text/html' ], [ $git->err ] ]; + } else { + $env->{'qspawn.filter'} = git_log_sed($self, $req); + [ 200, [ 'Content-Type', 'text/html' ] ]; + } + }); +} + +1; -- cgit v1.2.3-24-ge0c7