From f08dc18b4f5b3aa7a43bfa9ce754b23028f5babb Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 20 Jan 2016 22:35:23 +0000 Subject: [PATCH] repobrowse: commits with path redirect to root with anchor We shall save clients the overhead of making extra HTTP requests to follow partial paths. This ought to improve cache hit effectiveness on both the server and client side by reducing the potential different pages we may set. --- lib/PublicInbox/RepobrowseBase.pm | 40 +++++++++++++++++++++++++- lib/PublicInbox/RepobrowseGitCommit.pm | 30 +++++++------------ t/repobrowse_git_commit.t | 23 +++++++++++++++ 3 files changed, 73 insertions(+), 20 deletions(-) create mode 100644 t/repobrowse_git_commit.t diff --git a/lib/PublicInbox/RepobrowseBase.pm b/lib/PublicInbox/RepobrowseBase.pm index 14926d70c..dd854ffc5 100644 --- a/lib/PublicInbox/RepobrowseBase.pm +++ b/lib/PublicInbox/RepobrowseBase.pm @@ -60,7 +60,7 @@ sub mime_type { sub html_start { my ($self, $req, $title_html, $opts) = @_; my $desc = $req->{repo_info}->{desc_html}; - my $meta; + my $meta = ''; if ($opts) { my @robots; @@ -76,4 +76,42 @@ sub html_start { "
$desc";
 }
 
+sub r {
+	my ($self, $status, $req, @extra) = @_;
+	my @h;
+
+	my $body = '';
+	if ($status == 301 || $status == 302) {
+		# The goal is to be able to make redirects like we make
+		#  tags with '../'
+		my $cgi = $req->{cgi};
+		my $base;
+		$base = ref($cgi) eq 'CGI' ? $cgi->url(-base).'/' : $cgi->base;
+		my ($redir) = @extra;
+		if ($redir =~ m!\A\.\./!) { # relative redirect
+			my @orig = split(m!/+!, $cgi->path_info, -1);
+			shift @orig; # drop leading '/'
+			my @dest = split(m!/+!, $redir);
+
+			while ($dest[0] eq '..') {
+				pop @orig;
+				shift @dest;
+			}
+			my $end = '';
+			$end = pop @dest if $dest[-1] =~ /\A[#\?]/;
+			$redir = $base . join('/', @orig, @dest) . $end;
+		} else {
+			$redir = $base . '/' . $redir;
+		}
+		push @h, qw(Content-Type text/plain Location), $redir;
+
+		# mainly for curl (no-'-L') users:
+		$body = "Redirecting to $redir\n";
+	} else {
+		die "not implemented, yet: $status";
+	}
+
+	[ $status, \@h, [ $body ] ]
+}
+
 1;
diff --git a/lib/PublicInbox/RepobrowseGitCommit.pm b/lib/PublicInbox/RepobrowseGitCommit.pm
index d6843a6dd..edc277176 100644
--- a/lib/PublicInbox/RepobrowseGitCommit.pm
+++ b/lib/PublicInbox/RepobrowseGitCommit.pm
@@ -137,24 +137,23 @@ sub call_git_commit {
 	my $q = PublicInbox::RepobrowseGitQuery->new($req->{cgi});
 	my $id = $q->{id};
 	$id eq '' and $id = 'HEAD';
+
+	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));
+	}
+
 	my $git = $req->{repo_info}->{git};
 	my @cmd = (qw(show -z --numstat -p --encoding=UTF-8
 			--no-notes --no-color -c), $git->abbrev);
-	my @path;
-
-	# kill trailing slash
-	my $extra = $req->{extra};
-	if (@$extra) {
-		pop @$extra if $extra->[-1] eq '';
-		@path = (join('/', @$extra));
-		push @cmd, '--follow';
-	}
 
-	my $log = $git->popen(@cmd, GIT_FMT, $id, '--', @path);
+	my $log = $git->popen(@cmd, GIT_FMT, $id, '--');
 	my $H = <$log>;
 
 	# maybe the path didn't exist, yet, zip them back up
-	return git_commit_404($req, $q, $path[0]) unless defined $H;
+	return git_commit_404($req, $q) unless defined $H;
 	sub {
 		my ($res) = @_; # Plack callback
 		my $fh = $res->([200, ['Content-Type'=>'text/html']]);
@@ -164,19 +163,12 @@ sub call_git_commit {
 }
 
 sub git_commit_404 {
-	my ($req, $q, $path) = @_;
+	my ($req, $q) = @_;
 	my $x = 'Missing commit or path';
 	my $pfx = "$req->{relcmd}commit";
 
-	# print STDERR "path: $path\n";
 	my $try = 'try';
 	$x = "$x
$x\n\n";
-	if (defined $path) {
-		my $qs = $q->qs;
-		$x .= "" .
-			"try without the path $path\n";
-		$try = 'or';
-	}
 	my $qs = $q->qs(id => '');
 	$x .= "$try the latest commit in HEAD\n";
 	$x .= '
'; diff --git a/t/repobrowse_git_commit.t b/t/repobrowse_git_commit.t new file mode 100644 index 000000000..f13cd909e --- /dev/null +++ b/t/repobrowse_git_commit.t @@ -0,0 +1,23 @@ +# Copyright (C) 2016 all contributors +# License: AGPL-3.0+ +use strict; +use warnings; + +my $test = require './t/repobrowse_common_git.perl'; +test_psgi($test->{app}, sub { + my ($cb) = @_; + my $path = '/path/to/something'; + my $req = 'http://example.com/test.git/commit'; + my $res = $cb->(GET($req . $path)); + is($res->code, 301, 'got 301 to anchor'); + is($res->header('Location'), "$req#path:to:something", + 'redirected to anchor from path'); + + my $q = '?id=deadbeef'; + $res = $cb->(GET($req . $path . $q)); + is($res->code, 301, 'got 301 with query string'); + is($res->header('Location'), "$req$q#path:to:something", + 'redirected to anchor from path with query'); +}); + +done_testing(); -- 2.47.3