Subject: [PATCH] gitweb: Even more support for PATH_INFO based URLs

From: Jakub Narebski <[email-masked]>
Date: Wed, 20 Sep 2006 00:49:51 +0200

Now the following types of path based URLs are supported:

* project              overview (summary) page of project
* project/branch       shortlog of branch
* project/branch:file  file in branch, blob_plain view
* project/branch:dir/  directory listing of dir in branch, tree view

The following shortcuts works (see explanation below):

* project/branch:      directory listing of branch, main tree view
* project/:file        file in HEAD (raw)
* project/:dir/        directory listing of dir in HEAD
* project/:            directory listing of project's HEAD

We use ':' as separator between branch (ref) name and file name
(pathname) because valid branch (ref) name cannot have ':' inside.
This limit applies to branch name only. This allow for hierarchical
branches e.g. topic branch 'topic/subtopic', separate remotes
tracking branches e.g. 'refs/remotes/origin/HEAD', and discriminate
between head (branch) and tag with the same name.

Empty branch should be interpreted as HEAD.

If pathname (the part after ':') ends with '/', we assume that pathname
is name of directory, and we want to show contents of said directory
using "tree" view. If pathname is empty, it is equivalent to '/' (top
directory).

If pathname (the part after ':') does not end with '/', we assume that
pathname is name of file, and we show contents of said file using
"blob_plain" view.

Pathname is stripped of leading '/', so we can use ':/' to separate
branch from pathname. The rationale behind support for PATH_INFO based
URLs was to support project web pages for small projects: just create
an html branch and then use an URL like
  http://nowhere.com/gitweb.cgi/project.git/html:/index.html
The ':/' syntax allow for working links between .html files served
in such way, e.g. <a href="main.html"> link inside "index.html"
would get
  http://nowhere.com/gitweb.cgi/project.git/html:/main.html.

Signed-off-by: Jakub Narebski <[email-masked]>
---
Alternatively we could use git-cat-file -t (git_get_type subroutine)
to distinguish between trees and files (blobs), but first it would
slow gitweb somewhat (additional fork), and second git_get_type
assumes that $git_dir is set... and this variable is defined later!

See also:
  "[PATCH] gitweb: more support for PATH_INFO based URLs"
  Message-ID: <[email-masked]>
  http://permalink.gmane.org/gmane.comp.version-control.git/27133
(this email should be probably posted in this thread)

 gitweb/gitweb.perl |   29 ++++++++++++++++++++---------
 1 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 9445fa7..839b232 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -274,13 +274,16 @@ sub evaluate_path_info {
 	return if defined $project;
 	my $path_info = $ENV{"PATH_INFO"};
 	return if !$path_info;
-	$path_info =~ s,(^/|/$),,gs;
-	$path_info = validate_input($path_info);
+	$path_info =~ s,^/+,,;
 	return if !$path_info;
+	# find which part of PATH_INFO is project
 	$project = $path_info;
+	$project =~ s,/+$,,;
 	while ($project && !-e "$projectroot/$project/HEAD") {
 		$project =~ s,/*[^/]*$,,;
 	}
+	# validate project
+	$project = validate_input($project);
 	if (!$project ||
 	    ($export_ok && !-e "$projectroot/$project/$export_ok") ||
 	    ($strict_export && !project_in_list($project))) {
@@ -289,15 +292,23 @@ sub evaluate_path_info {
 	}
 	# do not change any parameters if an action is given using the query string
 	return if $action;
-	if ($path_info =~ m,^$project/([^/]+)/(.+)$,) {
-		# we got "project.git/branch/filename"
-		$action    ||= "blob_plain";
-		$hash_base ||= validate_input($1);
-		$file_name ||= validate_input($2);
-	} elsif ($path_info =~ m,^$project/([^/]+)$,) {
+	$path_info =~ s,^$project/*,,;
+	my ($refname, $pathname) = split(/:/, $path_info, 2);
+	if (defined $pathname) {
+		# we got "project.git/branch:filename" or "project.git/branch:dir/"
+		# we could use git_get_type(branch:pathname), but it needs $git_dir
+		$pathname =~ s,^/+,,;
+		if (!$pathname || substr($pathname, -1) eq "/") {
+			$action  ||= "tree";
+		} else {
+			$action  ||= "blob_plain";
+		}
+		$hash_base ||= validate_input($refname);
+		$file_name ||= validate_input($pathname);
+	} elsif (defined $refname) {
 		# we got "project.git/branch"
 		$action ||= "shortlog";
-		$hash   ||= validate_input($1);
+		$hash   ||= validate_input($refname);
 	}
 }
 evaluate_path_info();
-- 
1.4.2.1