From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.2 required=3.0 tests=ALL_TRUSTED,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, T_SCC_BODY_TEXT_LINE shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 7E9DD1F627 for ; Mon, 29 Aug 2022 09:26:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=80x24.org; s=selector1; t=1661765209; bh=NyelAlY/wsPsDBHgqPTAoBZbTxQk9s/ih8Vroi9bHs8=; h=From:To:Subject:Date:In-Reply-To:References:From; b=i/JJ7XWn2BYz6RRAMTyZppP/SgG8gjYjafksR5G9ucKP0iy9zRs2H20766LtdrLb7 hHR75zs/P9gTZmLeOSTAyFphjUfiG09K0HLOb9BI57CpYyLaUgnha6TdZzwiDHuG/T g3U99sRDfmbYTDOz0rPnixhHC7Q3ZKsuD1MD1YHI= From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 15/18] viewvcs: add tree view Date: Mon, 29 Aug 2022 09:26:44 +0000 Message-Id: <20220829092647.1512215-16-e@80x24.org> In-Reply-To: <20220829092647.1512215-1-e@80x24.org> References: <20220829092647.1512215-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: This also includes some glossary definitions to help users unfamiliar with git understand the relationship between trees and blobs. --- lib/PublicInbox/ViewDiff.pm | 14 +++--- lib/PublicInbox/ViewVCS.pm | 89 +++++++++++++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 9 deletions(-) diff --git a/lib/PublicInbox/ViewDiff.pm b/lib/PublicInbox/ViewDiff.pm index 8c1853e6..f16c7229 100644 --- a/lib/PublicInbox/ViewDiff.pm +++ b/lib/PublicInbox/ViewDiff.pm @@ -10,12 +10,11 @@ package PublicInbox::ViewDiff; use strict; use v5.10.1; use parent qw(Exporter); -our @EXPORT_OK = qw(flush_diff); +our @EXPORT_OK = qw(flush_diff uri_escape_path); use URI::Escape qw(uri_escape_utf8); use PublicInbox::Hval qw(ascii_html to_attr); use PublicInbox::Git qw(git_unquote); -my $UNSAFE = "^A-Za-z0-9\-\._~/"; # '/' + $URI::Escape::Unsafe{RFC3986} my $OID_NULL = '0{7,}'; my $OID_BLOB = '[a-f0-9]{7,}'; my $LF = qr!\n!; @@ -41,6 +40,11 @@ our $EXTRACT_DIFFS = qr/( ^\+{3}\x20($FN)$LF)/msx; our $IS_OID = qr/\A$OID_BLOB\z/s; +sub uri_escape_path { + # '/' + $URI::Escape::Unsafe{RFC3986} + uri_escape_utf8($_[0], "^A-Za-z0-9\-\._~/"); +} + # link to line numbers in blobs sub diff_hunk ($$$$) { my ($dst, $dctx, $ca, $cb) = @_; @@ -123,14 +127,14 @@ sub diff_header ($$$) { $pa = (split(m'/', git_unquote($pa), 2))[1] if $pa ne '/dev/null'; $pb = (split(m'/', git_unquote($pb), 2))[1] if $pb ne '/dev/null'; if ($pa eq $pb && $pb ne '/dev/null') { - $dctx->{Q} = "?b=".uri_escape_utf8($pb, $UNSAFE); + $dctx->{Q} = '?b='.uri_escape_path($pb); } else { my @q; if ($pb ne '/dev/null') { - push @q, 'b='.uri_escape_utf8($pb, $UNSAFE); + push @q, 'b='.uri_escape_path($pb); } if ($pa ne '/dev/null') { - push @q, 'a='.uri_escape_utf8($pa, $UNSAFE); + push @q, 'a='.uri_escape_path($pa); } $dctx->{Q} = '?'.join('&', @q); } diff --git a/lib/PublicInbox/ViewVCS.pm b/lib/PublicInbox/ViewVCS.pm index fd95e24e..a5545bcd 100644 --- a/lib/PublicInbox/ViewVCS.pm +++ b/lib/PublicInbox/ViewVCS.pm @@ -21,7 +21,7 @@ use PublicInbox::GitAsyncCat; use PublicInbox::WwwStream qw(html_oneshot); use PublicInbox::Linkify; use PublicInbox::Tmpfile; -use PublicInbox::ViewDiff qw(flush_diff); +use PublicInbox::ViewDiff qw(flush_diff uri_escape_path); use PublicInbox::View; use PublicInbox::Eml; use Text::Wrap qw(wrap); @@ -37,6 +37,14 @@ my $BIN_DETECT = 8000; # same as git my $SHOW_FMT = '--pretty=format:'.join('%n', '%P', '%p', '%H', '%T', '%s', '%f', '%an <%ae> %ai', '%cn <%ce> %ci', '%b%x00'); +my %GIT_MODE = ( + '100644' => ' ', # blob + '100755' => 'x', # executable blob + '040000' => 'd', # tree + '120000' => 'l', # symlink + '160000' => 'g', # commit (gitlink) +); + sub html_page ($$;@) { my ($ctx, $code) = @_[0, 1]; my $wcb = delete $ctx->{-wcb}; @@ -57,7 +65,7 @@ sub dbg_log ($) { return '
debug log read error
'; }; $ctx->{-linkify} //= PublicInbox::Linkify->new; - '
debug log:

'.
+	"
debug log:\n\n".
 		$ctx->{-linkify}->to_html($log).'
'; } @@ -95,7 +103,7 @@ sub stream_large_blob ($$) { $qsp->psgi_return($env, undef, \&stream_blob_parse_hdr, $ctx); } -sub show_other_result ($$) { # tag, tree, ... +sub show_other_result ($$) { # tag my ($bref, $ctx) = @_; if (my $qsp_err = delete $ctx->{-qsp_err}) { return html_page($ctx, 500, dbg_log($ctx) . @@ -296,7 +304,7 @@ sub show_other ($$) { my ($ctx, $res) = @_; my ($git, $oid, $type, $size) = @$res; $size > $MAX_SIZE and return html_page($ctx, 200, - "$oid is too big to show\n". dbg_log($ctx)); + ascii_html($type)." $oid is too big to show\n". dbg_log($ctx)); my $cmd = ['git', "--git-dir=$git->{git_dir}", qw(show --encoding=UTF-8 --no-color --no-abbrev), $oid ]; my $qsp = PublicInbox::Qspawn->new($cmd); @@ -304,6 +312,78 @@ sub show_other ($$) { $qsp->psgi_qx($ctx->{env}, undef, \&show_other_result, $ctx); } +sub show_tree_result ($$) { + my ($bref, $ctx) = @_; + if (my $qsp_err = delete $ctx->{-qsp_err}) { + return html_page($ctx, 500, dbg_log($ctx) . + "git ls-tree -z error:$qsp_err"); + } + my @ent = split(/\0/, $$bref); + my $qp = delete $ctx->{qp}; + my $l = $ctx->{-linkify} //= PublicInbox::Linkify->new; + my $pfx = $qp->{b}; + $$bref = "
tree $ctx->{tree_oid}";
+	if (defined $pfx) {
+		my $x = ascii_html($pfx);
+		$pfx .= '/';
+		$$bref .= qq(  path: $x\n);
+	} else {
+		$pfx = '';
+		$$bref .= qq[  (path unknown)\n];
+	}
+	my ($x, $m, $t, $oid, $sz, $f, $n);
+	$$bref .= "\n	size	name";
+	for (@ent) {
+		($x, $f) = split(/\t/, $_, 2);
+		undef $_;
+		($m, $t, $oid, $sz) = split(/ +/, $x, 4);
+		$m = $GIT_MODE{$m} // '?';
+		utf8::decode($f);
+		$n = ascii_html($f);
+		if ($m eq 'g') { # gitlink submodule commit
+			$$bref .= "\ng\t\t$n @ commit$oid";
+			next;
+		}
+		my $q = 'b='.ascii_html(uri_escape_path($pfx.$f));
+		if ($m eq 'd') { $n .= '/' }
+		elsif ($m eq 'x') { $n = "$n" }
+		elsif ($m eq 'l') { $n = "$n" }
+		$$bref .= qq(\n$m\t$sz\t$n);
+	}
+	$$bref .= dbg_log($ctx);
+	$$bref .= <glossary
+--------
+Tree objects belong to commits or other tree objects.  Trees may
+reference blobs, sub-trees, or commits of submodules.
+
+Path names are stored in tree objects, but trees do not know
+their own path name.  A tree's path name comes from their parent tree,
+or it is the root tree referenced by a commit object.  Thus, this web UI
+relies on the `b=' URI parameter as a hint to display the path name.
+
+Commit objects may be stored in trees to reference submodules.
+EOM + chop $$bref; + html_page($ctx, 200, $$bref); +} + +sub show_tree ($$) { + my ($ctx, $res) = @_; + my ($git, $oid, undef, $size) = @$res; + $size > $MAX_SIZE and return html_page($ctx, 200, + "tree $oid is too big to show\n". dbg_log($ctx)); + my $cmd = [ 'git', "--git-dir=$git->{git_dir}", + qw(ls-tree -z -l --no-abbrev), $oid ]; + my $qsp = PublicInbox::Qspawn->new($cmd); + $ctx->{tree_oid} = $oid; + $qsp->{qsp_err} = \($ctx->{-qsp_err} = ''); + $qsp->psgi_qx($ctx->{env}, undef, \&show_tree_result, $ctx); +} + # user_cb for SolverGit, called as: user_cb->($result_or_error, $uarg) sub solve_result { my ($res, $ctx) = @_; @@ -313,6 +393,7 @@ sub solve_result { my ($git, $oid, $type, $size, $di) = @$res; return show_commit($ctx, $res) if $type eq 'commit'; + return show_tree($ctx, $res) if $type eq 'tree'; return show_other($ctx, $res) if $type ne 'blob'; my $path = to_filename($di->{path_b} // $hints->{path_b} // 'blob'); my $raw_link = "(raw)";