diff options
author | Eric Wong <e@80x24.org> | 2016-01-12 21:25:17 +0000 |
---|---|---|
committer | Eric Wong <e@80x24.org> | 2016-04-05 18:58:27 +0000 |
commit | 6cc4db944fa85f97734ed93763dd745e8938b8e6 (patch) | |
tree | 7c6090156e2eab1bb95d59704cdd6b3ecd16f61d /lib | |
parent | acfa05d57be0a15a0d7357f66d97f877c02b5219 (diff) | |
download | public-inbox-6cc4db944fa85f97734ed93763dd745e8938b8e6.tar.gz |
We'll need this for displaying both annotated and lightweight tags.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/PublicInbox/RepoBrowse.pm | 2 | ||||
-rw-r--r-- | lib/PublicInbox/RepoBrowseGitTag.pm | 187 | ||||
-rw-r--r-- | lib/PublicInbox/RepoConfig.pm | 1 |
3 files changed, 189 insertions, 1 deletions
diff --git a/lib/PublicInbox/RepoBrowse.pm b/lib/PublicInbox/RepoBrowse.pm index 74fa40fc..ec54ed05 100644 --- a/lib/PublicInbox/RepoBrowse.pm +++ b/lib/PublicInbox/RepoBrowse.pm @@ -23,7 +23,7 @@ use warnings; use URI::Escape qw(uri_escape_utf8 uri_unescape); use PublicInbox::RepoConfig; -my %CMD = map { lc($_) => $_ } qw(Log Commit Tree Patch Blob Plain); +my %CMD = map { lc($_) => $_ } qw(Log Commit Tree Patch Blob Plain Tag); my %VCS = (git => 'Git'); my %LOADED; diff --git a/lib/PublicInbox/RepoBrowseGitTag.pm b/lib/PublicInbox/RepoBrowseGitTag.pm new file mode 100644 index 00000000..2019dd6f --- /dev/null +++ b/lib/PublicInbox/RepoBrowseGitTag.pm @@ -0,0 +1,187 @@ +# Copyright (C) 2016 all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> + +# shows the /tag/ endpoint for git repositories +package PublicInbox::RepoBrowseGitTag; +use strict; +use warnings; +use base qw(PublicInbox::RepoBrowseBase); +use POSIX qw(strftime); +use PublicInbox::Hval qw(utf8_html); + +my %cmd_map = ( # type => action + commit => 'commit', + tag => 'tag', + # tree/blob fall back to 'show' +); + +sub call_git_tag { + my ($self, $req) = @_; + + my $q = PublicInbox::RepoBrowseQuery->new($req->{cgi}); + my $h = $q->{h}; + $h eq '' and return sub { + my ($res) = @_; + git_tag_list($self, $req, $res); + }; + sub { + my ($res) = @_; + git_tag_show($self, $req, $h, $res); + } +} + +sub read_err { + my ($fh, $type, $hex) = @_; + + $fh->write("</pre><hr /><pre><b>error reading $type $hex</b>"); +} + +sub git_show_tag_as_tag { + my ($self, $fh, $req, $h, $cat, $left, $type, $hex) = @_; + my $buf = ''; + my $offset = 0; + while ($$left > 0) { + my $r = read($cat, $buf, $$left, $offset); + unless (defined $r) { + read_err($fh, $type, $hex); + last; + } + $offset += $r; + $$left -= $r; + } + my $head; + ($head, $buf) = split(/\r?\n\r?\n/, $buf, 2); + + my %h = map { split(/[ \t]/, $_, 2) } split(/\r?\n/, $head); + my $tag = utf8_html($h{tag}); + $type = $h{type} || '(unknown)'; + my $obj = $h{object}; + $h = $self->html_start($req, 'tag: ' . $tag); + my $label = "$type $obj"; + my $cmd = $cmd_map{$type} || 'show'; + my $rel = $req->{relcmd}; + my $obj_link = qq(<a\nhref="$rel$cmd?id=$obj">$label</a>); + $head = $h . "\n\n tag <b>$tag</b>\nobject $obj_link\n"; + if (my $tagger = $h{tagger}) { + $head .= 'tagger ' . join("\t", creator_split($tagger)) . "\n"; + } + $fh->write($head . "\n"); + + # n.b. tag subjects may not have a blank line after them, + # but we bold the first line anyways + my @buf = split(/\r?\n/s, $buf); + if (defined(my $subj = shift @buf)) { + $fh->write('<b>' . utf8_html($subj) . "</b>\n"); + + $fh->write(utf8_html($_) . "\n") foreach @buf; + } +} + +sub git_tag_show { + my ($self, $req, $h, $res) = @_; + my $git = $req->{repo_info}->{git}; + my $fh; + my $hdr = ['Content-Type', 'text/html; charset=UTF-8']; + + # yes, this could still theoretically show anything, + # but a tag could also point to anything: + $git->cat_file("refs/tags/$h", sub { + my ($cat, $left, $type, $hex) = @_; + $fh = $res->([200, $hdr]); + $h = PublicInbox::Hval->utf8($h); + my $m = "git_show_${type}_as_tag"; + + # git_show_tag_as_tag, git_show_commit_as_tag, + # git_show_tree_as_tag, git_show_blob_as_tag + if ($self->can($m)) { + $self->$m($fh, $req, $h, $cat, $left, $type, $hex); + } else { + $self->unknown_tag_type($fh, $req, $h, $type, $hex); + } + }); + unless ($fh) { + $fh = $res->([404, $hdr]); + $fh->write(invalid_tag_start($req, $h)); + } + $fh->write('</pre></body></html>'); + $fh->close; +} + +sub invalid_tag_start { + my ($self, $req, $h) = @_; + my $rel = $req->{relcmd}; + $h = 'missing tag: ' . utf8_html($h); + $self->html_start($req, $h) . "\n\n\t$h\n\n" . + qq(see <a\nhref="${rel}tag">tag list</a> for valid tags.); +} + +sub git_tag_list { + my ($self, $req, $res) = @_; + my $repo_info = $req->{repo_info}; + my $git = $repo_info->{git}; + my $desc = $repo_info->{desc_html}; + + # TODO: use Xapian so we can more easily handle offsets/limits + # for pagination instead of limiting + my $nr = 0; + my $count = 50; + my @cmd = (qw(for-each-ref --sort=-creatordate), + '--format=%(refname) %(creatordate:short) %(subject)', + "--count=$count", 'refs/tags/'); + my $refs = $git->popen(@cmd); + my $fh = $res->([200, ['Content-Type', 'text/html; charset=UTF-8']]); + + # tag names are unpredictable in length and requires tables :< + $fh->write($self->html_start($req, + "$repo_info->{path_info}: tag list") . + '</pre><table><tr>' . + join('', map { "<th><tt>$_</tt></th>" } qw(tag subject date)). + '</tr>'); + + foreach (<$refs>) { + my ($ref, $date, $s) = split(' ', $_, 3); + ++$nr; + $ref =~ s!\Arefs/tags/!!; + $ref = PublicInbox::Hval->utf8($ref); + my $h = $ref->as_html; + $ref = $ref->as_href; + $fh->write(qq(<tr><td><a\nhref="?h=$ref">$h</a></td><td>) . + utf8_html($s) . "</td><td>$date</td></tr>"); + } + my $end = ''; + if ($nr == $count) { + $end = "<pre>Showing the latest $nr tags</pre>"; + } + $fh->write("</table>$end</body></html>"); + $fh->close; +} + +sub unknown_tag_type { + my ($self, $fh, $req, $h, $type, $hex) = @_; + my $repo_info = $req->{repo_info}; + $h = $h->as_html; + my $rel = $req->{relcmd}; + my $label = "$type $hex"; + my $cmd = $cmd_map{$type} || 'show'; + my $obj_link = qq(<a\nhref="$rel$cmd?id=$hex">$label</a>\n); + + $fh->write($self->html_start($req, + "$repo_info->{path_info}: ref: $h") . + "\n\n <b>$h</b> (lightweight tag)\nobject $obj_link\n"); +} + +sub creator_split { + my ($tagger) = @_; + $tagger =~ s/\s*(\d+)(?:\s+([\+\-])?([ \d]{1,2})(\d\d))\z// or + return ($tagger, 0); + my ($tz_sign, $tz_H, $tz_M) = ($2, $3, $4); + my $sec = $1; + my $off = $tz_H * 3600 + $tz_M * 60; + $off *= -1 if $tz_sign eq '-'; + my @time = gmtime($sec + $off); + my $time = strftime('%Y-%m-%d %H:%M:%S', @time)." $tz_sign$tz_H$tz_M"; + + (utf8_html($tagger), $time); +} + +1; diff --git a/lib/PublicInbox/RepoConfig.pm b/lib/PublicInbox/RepoConfig.pm index dc99e7df..d55a27d2 100644 --- a/lib/PublicInbox/RepoConfig.pm +++ b/lib/PublicInbox/RepoConfig.pm @@ -36,6 +36,7 @@ sub lookup { my $path = $self->{"repo.$repo_path.path"}; (defined $path && -d $path) or return; $rv->{path} = $path; + $rv->{path_info} = $repo_path; foreach my $key (qw(description cloneurl)) { $rv->{$key} = try_cat("$path/$key"); |