From f6244586ba4f5a5e7575e1254be8c9bbe303fce9 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 15 Feb 2017 22:35:18 +0000 Subject: repobrowse: switch to new URL format to avoid query strings Query strings make endpoint caching more difficult since they're order-independent. They are also more likely lost or truncated inadvertantly when copy+pasting, so try to avoid them for default endpoints. There's still some things which are broken and followup commits will be needed to fix them. --- lib/PublicInbox/RepoBase.pm | 1 - lib/PublicInbox/RepoGit.pm | 6 ++-- lib/PublicInbox/RepoGitAtom.pm | 15 +++++----- lib/PublicInbox/RepoGitBlob.pm | 5 +--- lib/PublicInbox/RepoGitCommit.pm | 33 ++++++++-------------- lib/PublicInbox/RepoGitDiff.pm | 5 +--- lib/PublicInbox/RepoGitDiffCommon.pm | 7 ++--- lib/PublicInbox/RepoGitLog.pm | 27 ++++++++++-------- lib/PublicInbox/RepoGitPatch.pm | 7 ++--- lib/PublicInbox/RepoGitPlain.pm | 5 +--- lib/PublicInbox/RepoGitQuery.pm | 2 +- lib/PublicInbox/RepoGitSnapshot.pm | 4 +-- lib/PublicInbox/RepoGitSummary.pm | 14 +++++----- lib/PublicInbox/RepoGitTag.pm | 11 ++++---- lib/PublicInbox/RepoGitTree.pm | 54 ++++++++++++++++-------------------- lib/PublicInbox/Repobrowse.pm | 12 +++++--- 16 files changed, 93 insertions(+), 115 deletions(-) (limited to 'lib') diff --git a/lib/PublicInbox/RepoBase.pm b/lib/PublicInbox/RepoBase.pm index 97f13b25..668e9711 100644 --- a/lib/PublicInbox/RepoBase.pm +++ b/lib/PublicInbox/RepoBase.pm @@ -3,7 +3,6 @@ package PublicInbox::RepoBase; use strict; use warnings; -require PublicInbox::RepoGitQuery; use PublicInbox::Hval; our %MIME_TYPE_WHITELIST = ('application/pdf' => 1); diff --git a/lib/PublicInbox/RepoGit.pm b/lib/PublicInbox/RepoGit.pm index b44457ca..114eb656 100644 --- a/lib/PublicInbox/RepoGit.pm +++ b/lib/PublicInbox/RepoGit.pm @@ -50,17 +50,17 @@ sub git_dec_links ($$) { $h = PublicInbox::Hval->utf8($h); my $r = $h->as_href; $h = $h->as_html; - push @l, qq($s -> $h); + push @l, qq($s -> $h); } elsif (s/\Atag: //) { my $h = PublicInbox::Hval->utf8($_); my $r = $h->as_href; $h = $h->as_html; - push @l, qq($h); + push @l, qq($h); } else { my $h = PublicInbox::Hval->utf8($_); my $r = $h->as_href; $h = $h->as_html; - push @l, qq($h); + push @l, qq($h); } } @l; diff --git a/lib/PublicInbox/RepoGitAtom.pm b/lib/PublicInbox/RepoGitAtom.pm index 6d0caa02..cf4df11c 100644 --- a/lib/PublicInbox/RepoGitAtom.pm +++ b/lib/PublicInbox/RepoGitAtom.pm @@ -46,7 +46,7 @@ sub flush_hdr ($$$) { $$dst .= ''; $$dst .= qq({q}->{h}"); + $title = utf8_html("$title, $req->{-tip}"); my $url = repo_root_url($self, $req); my $hdr = {}; my $subtitle = $repo_info->desc_html; @@ -76,7 +76,7 @@ sub git_atom_sed ($$) { qq($title) . qq($subtitle) . qq(); - my ($plinks, $id, $ai); + my ($plinks, $ai); my $end = ''; my $blines; sub { @@ -140,12 +140,11 @@ sub call_git_atom { my $git = $repo_info->{git}; my $env = $req->{env}; - my $q =$req->{'q'} = PublicInbox::RepoGitQuery->new($env); - my $h = $q->{h}; + my $tip = $req->{-tip}; my $read_log = sub { my $cmd = $git->cmd(qw(log --no-notes --no-color --abbrev-commit), $git->abbrev, - $ATOM_FMT, "-$max", $h, '--'); + $ATOM_FMT, "-$max", $tip, '--'); my $expath = $req->{expath}; push @$cmd, $expath if $expath ne ''; my $rdr = { 2 => $git->err_begin }; @@ -155,13 +154,13 @@ sub call_git_atom { sub { $env->{'qspawn.response'} = $_[0]; - return $read_log->() if $h ne ''; + return $read_log->() if $tip ne ''; my $cmd = $git->cmd(qw(symbolic-ref --short HEAD)); my $rdr = { 2 => $git->err_begin }; my $qsp = PublicInbox::Qspawn->new($cmd, undef, undef, $rdr); $qsp->psgi_qx($env, undef, sub { - chomp($h = ${$_[0]}); + chomp($tip = ${$_[0]}); $read_log->(); }) } diff --git a/lib/PublicInbox/RepoGitBlob.pm b/lib/PublicInbox/RepoGitBlob.pm index 586b4acc..f9c28c22 100644 --- a/lib/PublicInbox/RepoGitBlob.pm +++ b/lib/PublicInbox/RepoGitBlob.pm @@ -12,10 +12,7 @@ our @EXPORT = qw(git_blob_mime_type git_blob_stream_response); sub call_git_blob { my ($self, $req) = @_; my $git = $req->{repo_info}->{git}; - my $q = PublicInbox::RepoGitQuery->new($req->{env}); - my $id = $q->{id}; - $id eq '' and $id = 'HEAD'; - $id .= ":$req->{expath}"; + my $id = $req->{-tip} . ':' . $req->{expath}; my ($cat, $hex, $type, $size) = $git->cat_file_begin($id); return unless defined $cat; diff --git a/lib/PublicInbox/RepoGitCommit.pm b/lib/PublicInbox/RepoGitCommit.pm index e98c3c18..c1cf06db 100644 --- a/lib/PublicInbox/RepoGitCommit.pm +++ b/lib/PublicInbox/RepoGitCommit.pm @@ -37,11 +37,9 @@ sub commit_header { my @p = split(' ', $p); my $rel = $req->{relcmd}; - my $q = $req->{'q'}; - my $qs = $req->{qs} = $q->qs(id => $h); my $x = $self->html_start($req, $s) . "\n" . - qq( commit $H (patch)\n) . - qq( tree $t); + qq( commit $H (patch)\n) . + qq( tree $t); my $git = $req->{repo_info}->{git}; # extra show path information, if any @@ -56,7 +54,7 @@ sub commit_header { my $e = PublicInbox::Hval->utf8($_, join('/', @t)); $ep = $e->as_path; my $eh = $e->as_html; - $ep = "${rel}tree/$ep?id=$h"; + $ep = "${rel}tree/$ep/$h"; qq($eh); } @$extra); $path = "/$ep"; @@ -66,10 +64,10 @@ sub commit_header { my $np = scalar @p; if ($np == 1) { my $p = $p[0]; - $x .= git_parent_line(' parent', $p, $q, $git, $rel, $path); + $x .= git_parent_line(' parent', $p, $git, $rel); } elsif ($np > 1) { $req->{mhelp} = CC_MERGE; - my @common = ($q, $git, $rel, $path); + my @common = ($git, $rel); my @t = @p; my $p = shift @t; $x .= git_parent_line(' parents', $p, @common); @@ -119,24 +117,20 @@ sub git_commit_sed ($$) { sub call_git_commit { # RepoBase calls this my ($self, $req) = @_; my $env = $req->{env}; - my $q = PublicInbox::RepoGitQuery->new($env); - my $id = $q->{id}; - $id eq '' and $id = 'HEAD'; + my $tip = $req->{-tip}; my $expath = $req->{expath}; if ($expath ne '') { my $relup = join('', map { '../' } @{$req->{extra}}); - my $qs = $q->qs; - return $self->r(301, $req, "$relup$qs#".to_attr($expath)); + return $self->r(301, $req, "$relup#".to_attr($expath)); } my $git = $req->{repo_info}->{git}; my $cmd = $git->cmd(qw(show -z --numstat -p --encoding=UTF-8 --no-notes --no-color -c), - $git->abbrev, GIT_FMT, $id, '--'); + $git->abbrev, GIT_FMT, $tip, '--'); my $rdr = { 2 => $git->err_begin }; my $qsp = PublicInbox::Qspawn->new($cmd, undef, $rdr); - $req->{'q'} = $q; $env->{'qspawn.quiet'} = 1; $qsp->psgi_return($env, undef, sub { # parse header my ($r, $bref) = @_; @@ -159,8 +153,7 @@ sub git_commit_404 { my $try = 'try'; $x = "$x
$x\n\n";
-	my $qs = $req->{'q'}->qs(id => '');
-	$x .= "$try the latest commit in HEAD\n";
+	$x .= "$try the latest commit in HEAD\n";
 	$x .= '
'; [404, ['Content-Type'=>'text/html'], [ $x ]]; @@ -168,11 +161,10 @@ sub git_commit_404 { # FIXME: horrifically expensive... sub git_parent_line { - my ($pfx, $p, $q, $git, $rel, $path) = @_; - my $qs = $q->qs(id => $p); + my ($pfx, $p, $git, $rel) = @_; my $t = git_commit_title($git, $p); $t = defined $t ? utf8_html($t) : ''; - $pfx . qq( $p $t\n); + $pfx . qq( $p $t\n); } # do not break anchor links if the combined diff doesn't show changes: @@ -186,14 +178,13 @@ sub show_unchanged { qq( See the parents, ) . "or view final state(s) below:\n\n"; my $rel = $req->{relcmd}; - my $qs = $req->{qs}; foreach my $anchor (@unchanged) { my $fn = $anchors->{$anchor}; my $p = PublicInbox::Hval->utf8(git_unquote($fn)); $p = $p->as_path; $fn = utf8_html($fn); $$dst .= qq(\t); + $$dst .= qq(\nid="$anchor"\nhref="${rel}tree/$p">); $$dst .= "$fn\n"; } } diff --git a/lib/PublicInbox/RepoGitDiff.pm b/lib/PublicInbox/RepoGitDiff.pm index bb71e738..ef4717ac 100644 --- a/lib/PublicInbox/RepoGitDiff.pm +++ b/lib/PublicInbox/RepoGitDiff.pm @@ -34,11 +34,8 @@ sub git_diff_sed ($$) { sub call_git_diff { my ($self, $req) = @_; + my ($id, $id2) = split(/\.\./, $req->{h}); my $env = $req->{env}; - my $q = PublicInbox::RepoGitQuery->new($env); - my $id = $q->{id}; - my $id2 = $q->{id2}; - my $git = $req->{repo_info}->{git}; my $cmd = $git->cmd(qw(diff-tree -z --numstat -p --encoding=UTF-8 --no-color -M -B -D -r), $id2, $id, '--'); diff --git a/lib/PublicInbox/RepoGitDiffCommon.pm b/lib/PublicInbox/RepoGitDiffCommon.pm index 0604f9dd..3e3ea4ee 100644 --- a/lib/PublicInbox/RepoGitDiffCommon.pm +++ b/lib/PublicInbox/RepoGitDiffCommon.pm @@ -68,17 +68,16 @@ sub git_diff_ab_hunk ($$$$) { $na = defined $na ? "#n$na" : ''; my $p = $req->{p}->[0]; $rv .= qq({path_a}?id=$p$na">); + $rv .= qq(\nhref="${rel}tree/$p/$req->{path_a}$na">); $rv .= "$ca"; } $rv .= ' '; if (defined($nb) && $nb == 0) { # deleted file $rv .= $cb; } else { - my $h = $req->{h}; $nb = defined $nb ? "#n$nb" : ''; $rv .= qq({path_b}?id=$h$nb">); + $rv .= qq(\nhref="${rel}tree/$req->{-tip}/$req->{path_b}$nb">); $rv .= "$cb"; } $rv . ' @@' . utf8_html($ctx); @@ -129,7 +128,7 @@ sub git_diff_cc_hunk { } else { my $h = $req->{h}; $rv .= qq( $last); + $rv .= qq(\nhref="${rel}tree/$h/$path#n$n">$last); } $rv .= " $at" . utf8_html($ctx); } diff --git a/lib/PublicInbox/RepoGitLog.pm b/lib/PublicInbox/RepoGitLog.pm index 9cfa526e..09409edd 100644 --- a/lib/PublicInbox/RepoGitLog.pm +++ b/lib/PublicInbox/RepoGitLog.pm @@ -27,18 +27,18 @@ sub parent_links { sub flush_log_hdr ($$$) { my ($req, $dst, $hdr) = @_; - my $rel = $req->{relcmd}; + my $lpfx = $req->{lpfx}; my $seen = $req->{seen}; $$dst .= '
' if scalar keys %$seen;
 	my $id = $hdr->{h};
 	$seen->{$id} = 1;
 	$$dst .= qq();
+	$$dst .= qq(href="${lpfx}commit/$id">);
 	$$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)) . ')';
+		$$dst .= ' (' . join(', ', git_dec_links($lpfx, $D)) . ')';
 	}
 	my @p = split(/ /, $hdr->{p});
 	push @{$req->{parents}}, @p;
@@ -56,14 +56,14 @@ sub git_log_sed_end ($$) {
 	my $np = 0;
 	my $seen = $req->{seen};
 	my $git = $req->{repo_info}->{git};
-	my $rel = $req->{relcmd};
+	my $lpfx = $req->{lpfx};
 	foreach my $p (@{$req->{parents}}) {
 		next if $seen->{$p};
 		$seen->{$p} = ++$np;
 		my $s = git_commit_title($git, $p);
-		$m .= qq(\n$p\t);
+		$m .= qq(\n$p\t);
 		$s = defined($s) ? utf8_html($s) : '';
-		$m .= qq($s);
+		$m .= qq($s);
 	}
 	if ($np == 0) {
 		$$dst .= "No commits follow";
@@ -123,17 +123,22 @@ sub call_git_log {
 	my ($self, $req) = @_;
 	my $repo_info = $req->{repo_info};
 	my $max = $repo_info->{max_commit_count} || 50;
+	my $h = $req->{h};
 	$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, '--');
+				$git->abbrev, $LOG_FMT, "-$max",
+				$req->{-tip}, '--');
 	my $rdr = { 2 => $git->err_begin };
-	my $title = "log: $repo_info->{repo} (" . utf8_html($h). ')';
+	my $title = "log: $repo_info->{repo}";
+	if (defined $h) {
+		$title .= ' ('. utf8_html($h). ')';
+		$req->{lpfx} = $req->{relcmd};
+	} else {
+		$req->{lpfx} = $req->{relcmd}.$req->{-tip};
+	}
 	$req->{lhtml} = $self->html_start($req, $title) . "\n\n";
 	my $qsp = PublicInbox::Qspawn->new($cmd, undef, $rdr);
 	$qsp->psgi_return($env, undef, sub {
diff --git a/lib/PublicInbox/RepoGitPatch.pm b/lib/PublicInbox/RepoGitPatch.pm
index e9227b6f..d851457c 100644
--- a/lib/PublicInbox/RepoGitPatch.pm
+++ b/lib/PublicInbox/RepoGitPatch.pm
@@ -17,13 +17,12 @@ sub call_git_patch {
 	my ($self, $req) = @_;
 	my $git = $req->{repo_info}->{git};
 	my $env = $req->{env};
-	my $q = PublicInbox::RepoGitQuery->new($env);
-	my $id = $q->{id};
-	$id =~ /\A[\w-]+([~\^][~\^\d])*\z/ or $id = 'HEAD';
+	my $tip = $req->{-tip};
+	$tip =~ /\A[\w-]+([~\^][~\^\d])*\z/;
 
 	# limit scope, don't take extra args to avoid wasting server
 	# resources buffering:
-	my $range = "$id~1..$id^0";
+	my $range = "$tip~1..$tip^0";
 	my $cmd = $git->cmd(@CMD, $sig." $range", $range, '--');
 	my $expath = $req->{expath};
 	push @$cmd, $expath if $expath ne '';
diff --git a/lib/PublicInbox/RepoGitPlain.pm b/lib/PublicInbox/RepoGitPlain.pm
index 2ba24e08..6114a858 100644
--- a/lib/PublicInbox/RepoGitPlain.pm
+++ b/lib/PublicInbox/RepoGitPlain.pm
@@ -11,10 +11,7 @@ use PublicInbox::Qspawn;
 sub call_git_plain {
 	my ($self, $req) = @_;
 	my $git = $req->{repo_info}->{git};
-	my $q = PublicInbox::RepoGitQuery->new($req->{env});
-	my $id = $q->{id};
-	$id eq '' and $id = 'HEAD';
-	$id .= ":$req->{expath}";
+	my $id = $req->{-tip} . ':' . $req->{expath};
 	my ($cat, $hex, $type, $size) = $git->cat_file_begin($id);
 	return unless defined $cat;
 
diff --git a/lib/PublicInbox/RepoGitQuery.pm b/lib/PublicInbox/RepoGitQuery.pm
index 638a1316..c8d4a256 100644
--- a/lib/PublicInbox/RepoGitQuery.pm
+++ b/lib/PublicInbox/RepoGitQuery.pm
@@ -7,7 +7,7 @@ use strict;
 use warnings;
 use PublicInbox::Hval;
 use URI::Escape qw(uri_unescape);
-my @KNOWN_PARAMS = qw(id id2 h ofs);
+my @KNOWN_PARAMS = qw(id id2 ofs);
 
 sub new {
 	my ($class, $env) = @_;
diff --git a/lib/PublicInbox/RepoGitSnapshot.pm b/lib/PublicInbox/RepoGitSnapshot.pm
index e05ad80c..9ba4c04a 100644
--- a/lib/PublicInbox/RepoGitSnapshot.pm
+++ b/lib/PublicInbox/RepoGitSnapshot.pm
@@ -36,9 +36,7 @@ our %FMT_TYPES = (
 sub call_git_snapshot ($$) { # invoked by PublicInbox::RepoBase::call
 	my ($self, $req) = @_;
 
-	my @extra = @{$req->{extra}};
-	my $ref = shift @extra;
-	return $self->r(404) if (!defined $ref) || scalar(@extra);
+	my $ref = $req->{-tip};
 	my $orig_fn = $ref;
 
 	# just in case git changes refname rules, don't allow wonky filenames
diff --git a/lib/PublicInbox/RepoGitSummary.pm b/lib/PublicInbox/RepoGitSummary.pm
index e9e1458b..0ecef981 100644
--- a/lib/PublicInbox/RepoGitSummary.pm
+++ b/lib/PublicInbox/RepoGitSummary.pm
@@ -58,10 +58,10 @@ sub for_each_ref {
 		my $sref;
 		if ($type eq 'tag') {
 			$h = "$h";
-			$sref = $ref = $rel . 'tag?h=' . $ref;
+			$sref = $ref = $rel . 'tag/' . $ref;
 		} elsif ($type eq 'commit') {
-			$sref = $rel . 'commit?h=' . $ref;
-			$ref = $rel . 'log?h=' . $ref;
+			$sref = $rel . 'commit/' . $ref;
+			$ref = $rel . 'log/' . $ref;
 		} else {
 			# no point in wasting code to support tagged
 			# trees/blobs...
@@ -82,7 +82,7 @@ sub for_each_ref {
 	foreach my $r (@$readme) {
 		my $doc = $git->cat_file('HEAD:'.$r);
 		defined $doc or next;
-		$fh->write('
' . readme_path_links($rel, $r) .
+		$fh->write('
' . readme_path_links($req, $rel, $r) .
 			" (HEAD)\n\n" . utf8_html($$doc) . '
'); } $fh->write(''); @@ -90,17 +90,17 @@ sub for_each_ref { } sub readme_path_links { - my ($rel, $readme) = @_; + my ($req, $rel, $readme) = @_; my @path = split(m!/+!, $readme); - my $s = "tree root/"; + my $s = "tree {-tip}\">root/"; my @t; $s .= join('/', (map { push @t, $_; my $e = PublicInbox::Hval->utf8($_, join('/', @t)); my $ep = $e->as_path; my $eh = $e->as_html; - $e = "$eh"; + $e = "{-tip}/$ep\">$eh"; # bold the last one scalar(@t) == scalar(@path) ? "$e" : $e; } @path)); diff --git a/lib/PublicInbox/RepoGitTag.pm b/lib/PublicInbox/RepoGitTag.pm index 96835b2c..d046f853 100644 --- a/lib/PublicInbox/RepoGitTag.pm +++ b/lib/PublicInbox/RepoGitTag.pm @@ -19,9 +19,8 @@ my %cmd_map = ( # type => action sub call_git_tag { my ($self, $req) = @_; - my $q = PublicInbox::RepoGitQuery->new($req->{env}); - my $h = $q->{h}; - $h eq '' and return git_tag_list($self, $req); + my $h = $req->{h}; + defined $h or return git_tag_list($self, $req); sub { my ($res) = @_; git_tag_show($self, $req, $h, $res); @@ -58,7 +57,7 @@ sub git_show_tag_as_tag { my $label = "$type $obj"; my $cmd = $cmd_map{$type} || 'show'; my $rel = $req->{relcmd}; - my $obj_link = qq($label); + my $obj_link = qq($label); $head = $h . "\n\n tag $tag\nobject $obj_link\n"; if (my $tagger = $h{tagger}) { $head .= 'tagger ' . join("\t", creator_split($tagger)) . "\n"; @@ -147,7 +146,7 @@ sub git_each_tag_sed ($$) { my $h = $ref->as_html; $ref = $ref->as_href; $dst .= qq() . - qq($h) . + qq($h) . qq($date) . utf8_html($s) . ''; } @@ -189,7 +188,7 @@ sub unknown_tag_type { my $rel = $req->{relcmd}; my $label = "$type $hex"; my $cmd = $cmd_map{$type} || 'show'; - my $obj_link = qq($label\n); + my $obj_link = qq($label\n); $fh->write($self->html_start($req, "$repo_info->{repo}: ref: $h") . diff --git a/lib/PublicInbox/RepoGitTree.pm b/lib/PublicInbox/RepoGitTree.pm index 4a68cf69..840af9ad 100644 --- a/lib/PublicInbox/RepoGitTree.pm +++ b/lib/PublicInbox/RepoGitTree.pm @@ -21,14 +21,7 @@ sub call_git_tree { my ($self, $req) = @_; my @extra = @{$req->{extra}}; my $git = $req->{repo_info}->{git}; - my $q = PublicInbox::RepoGitQuery->new($req->{env}); - my $id = $q->{id}; - if ($id eq '') { - chomp($id = $git->qx(qw(rev-parse --short=10 HEAD))); - $q->{id} = $id; - } - - my $obj = "$id:$req->{expath}"; + my $obj = "$req->{-tip}:$req->{expath}"; my ($hex, $type, $size) = $git->check($obj); unless (defined($type)) { @@ -41,7 +34,7 @@ sub call_git_tree { if ($type eq 'tree') { $opts->{noindex} = 1; $req->{thtml} = $self->html_start($req, $title, $opts) . "\n"; - git_tree_show($req, $hex, $q); + git_tree_show($req, $hex); } elsif ($type eq 'blob') { sub { my $res = $_[0]; @@ -49,7 +42,7 @@ sub call_git_tree { ['Content-Type','text/html; charset=UTF-8']]); $fh->write($self->html_start($req, $title, $opts) . "\n"); - git_blob_show($req, $fh, $git, $hex, $q); + git_blob_show($req, $fh, $git, $hex); $fh->write(''); $fh->close; } @@ -60,15 +53,15 @@ sub call_git_tree { } sub cur_path { - my ($req, $q) = @_; - my $qs = $q->qs; + my ($req) = @_; my @ex = @{$req->{extra}} or return 'root'; my $s; + my $tip = $req->{-tip}; my $rel = $req->{relcmd}; # avoid relative paths, here, we don't want to propagate # trailing-slash URLs although we tolerate them - $s = "root/"; + $s = "root/"; my $cur = pop @ex; my @t; $s .= join('/', (map { @@ -76,37 +69,38 @@ sub cur_path { my $e = PublicInbox::Hval->utf8($_, join('/', @t)); my $ep = $e->as_path; my $eh = $e->as_html; - "$eh"; + "$eh"; } @ex), ''.utf8_html($cur).''); } sub git_blob_show { - my ($req, $fh, $git, $hex, $q) = @_; + my ($req, $fh, $git, $hex) = @_; # ref: buffer_is_binary in git.git my $to_read = 8000; # git uses this size to detect binary files my $text_p; my $n = 0; + my $tip = $req->{-tip}; my $rel = $req->{relcmd}; - my $plain = join('/', "${rel}plain", @{$req->{extra}}); - $plain = PublicInbox::Hval->utf8($plain)->as_path . $q->qs; - my $t = cur_path($req, $q); - my $h = qq{\npath: $t\n\nblob $hex}; + my $plain = join('/', "${rel}plain/$tip", @{$req->{extra}}); + $plain = PublicInbox::Hval->utf8($plain)->as_path; + my $t = cur_path($req); + my $s = qq{\npath: $t\n\nblob $hex}; my $end = ''; $git->cat_file($hex, sub { my ($cat, $left) = @_; # $$left == $size - $h .= qq{\t$$left bytes (raw)}; + $s .= qq{\t$$left bytes (raw)}; $to_read = $$left if $to_read > $$left; my $r = read($cat, my $buf, $to_read); return unless defined($r) && $r > 0; $$left -= $r; if (index($buf, "\0") >= 0) { - $fh->write("$h\n$BINARY_MSG
"); + $fh->write("$s\n$BINARY_MSG
"); return; } - $fh->write($h."
");
+		$fh->write($s."

");
 		$text_p = 1;
 
 		while (1) {
@@ -147,7 +141,6 @@ sub git_tree_sed ($) {
 	my ($req) = @_;
 	my @lines;
 	my $buf = '';
-	my $qs = $req->{qs};
 	my $pfx = $req->{tpfx};
 	my $end;
 	sub {
@@ -180,29 +173,30 @@ sub git_tree_sed ($) {
 			# 'plain' and 'log' links intentionally omitted
 			# for brevity and speed
 			$dst .= qq($m\t).
-				qq($s\t$path\n);
+				qq($s\t$path\n);
 		}
 		$dst;
 	}
 }
 
 sub git_tree_show {
-	my ($req, $hex, $q) = @_;
+	my ($req, $hex) = @_;
 	my $git = $req->{repo_info}->{git};
 	my $cmd = $git->cmd(qw(ls-tree -l -z), $git->abbrev, $hex);
 	my $rdr = { 2 => $git->err_begin };
 	my $qsp = PublicInbox::Qspawn->new($cmd, undef, $rdr);
-	my $t = cur_path($req, $q);
+	my $t = cur_path($req);
 	my $pfx;
 
 	$req->{thtml} .= "\npath: $t\n\nmode\tsize\tname\n";
-	$req->{qs} = $q->qs;
 	if ($req->{tslash}) {
-		$pfx = './';
+		$pfx = '../';
 	} elsif (defined(my $last = $req->{extra}->[-1])) {
-		$pfx = PublicInbox::Hval->utf8($last)->as_path . '/';
+		$pfx = PublicInbox::Hval->utf8($last)->as_path;
+	} elsif (defined $req->{h}) {
+		$pfx = $req->{-tip};
 	} else {
-		$pfx = 'tree/';
+		$pfx = 'tree/' . $req->{-tip};
 	}
 	$req->{tpfx} = $pfx;
 	my $env = $req->{env};
diff --git a/lib/PublicInbox/Repobrowse.pm b/lib/PublicInbox/Repobrowse.pm
index c16f10fd..2513a105 100644
--- a/lib/PublicInbox/Repobrowse.pm
+++ b/lib/PublicInbox/Repobrowse.pm
@@ -89,7 +89,7 @@ sub call {
 	my $method = $env->{REQUEST_METHOD};
 	return r(405, 'Method Not Allowed') if ($method !~ /\AGET|HEAD|POST\z/);
 
-	# URL syntax: / repo [ / cmd [ / path ] ]
+	# URL syntax: / repo [ / cmd [ / head [ / path ] ] ]
 	# cmd: log | commit | diff | tree | view | blob | snapshot
 	# repo and path (@extra) may both contain '/'
 	my $path_info = $env->{PATH_INFO};
@@ -116,13 +116,16 @@ sub call {
 	my $vcs_lc = $repo_info->{vcs};
 	my $vcs = $VCS{$vcs_lc} or return r404();
 	my $mod;
+	my $h;
 	if (defined $cmd && length $cmd) {
 		$mod = $CMD{$cmd};
-		unless ($mod) {
+		if ($mod) {
+			$h = shift @extra if @extra;
+		} else {
 			unshift @extra, $cmd;
 			$mod = 'Fallback';
 		}
-		$req->{relcmd} = '../' x scalar(@extra);
+		$req->{relcmd} = '../' x (scalar(@extra) + 1);
 	} else {
 		$mod = 'Summary';
 		$cmd = 'summary';
@@ -137,7 +140,8 @@ sub call {
 		pop @extra;
 		++$tslash;
 	}
-
+	$req->{h} = $h;
+	$req->{-tip} = defined $h ? $h : 'HEAD';
 	return no_tslash($env) if ($tslash && $NO_TSLASH{$mod});
 
 	$req->{tslash} = $tslash;
-- 
cgit v1.2.3-24-ge0c7