revision traversal: '--simplify-by-decoration'
[gitweb.git] / gitweb / gitweb.perl
index 50604e0a0c25da6b04068ca339f3ac113c8a62b1..63c793ec398f0c378c3e3ec94b2d5675bdff418e 100755 (executable)
@@ -549,7 +549,12 @@ sub evaluate_path_info {
                'history',
        );
 
-       my ($refname, $pathname) = split(/:/, $path_info, 2);
+       # we want to catch
+       # [$hash_parent_base[:$file_parent]..]$hash_parent[:$file_name]
+       my ($parentrefname, $parentpathname, $refname, $pathname) =
+               ($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?(.+?)(?::(.+))?$/);
+
+       # first, analyze the 'current' part
        if (defined $pathname) {
                # we got "branch:filename" or "branch:dir/"
                # we could use git_get_type(branch:pathname), but:
@@ -564,7 +569,13 @@ sub evaluate_path_info {
                        $input_params{'action'} ||= "tree";
                        $pathname =~ s,/$,,;
                } else {
-                       $input_params{'action'} ||= "blob_plain";
+                       # the default action depends on whether we had parent info
+                       # or not
+                       if ($parentrefname) {
+                               $input_params{'action'} ||= "blobdiff_plain";
+                       } else {
+                               $input_params{'action'} ||= "blob_plain";
+                       }
                }
                $input_params{'hash_base'} ||= $refname;
                $input_params{'file_name'} ||= $pathname;
@@ -584,6 +595,27 @@ sub evaluate_path_info {
                        $input_params{'hash'} ||= $refname;
                }
        }
+
+       # next, handle the 'parent' part, if present
+       if (defined $parentrefname) {
+               # a missing pathspec defaults to the 'current' filename, allowing e.g.
+               # someproject/blobdiff/oldrev..newrev:/filename
+               if ($parentpathname) {
+                       $parentpathname =~ s,^/+,,;
+                       $parentpathname =~ s,/$,,;
+                       $input_params{'file_parent'} ||= $parentpathname;
+               } else {
+                       $input_params{'file_parent'} ||= $input_params{'file_name'};
+               }
+               # we assume that hash_parent_base is wanted if a path was specified,
+               # or if the action wants hash_base instead of hash
+               if (defined $input_params{'file_parent'} ||
+                       grep { $_ eq $input_params{'action'} } @wants_base) {
+                       $input_params{'hash_parent_base'} ||= $parentrefname;
+               } else {
+                       $input_params{'hash_parent'} ||= $parentrefname;
+               }
+       }
 }
 evaluate_path_info();
 
@@ -734,7 +766,8 @@ (%)
                # try to put as many parameters as possible in PATH_INFO:
                #   - project name
                #   - action
-               #   - hash or hash_base:filename
+               #   - hash_parent or hash_parent_base:/file_parent
+               #   - hash or hash_base:/filename
 
                # When the script is the root DirectoryIndex for the domain,
                # $href here would be something like http://gitweb.example.com/
@@ -753,17 +786,36 @@ (%)
                        delete $params{'action'};
                }
 
-               # Finally, we put either hash_base:file_name or hash
+               # Next, we put hash_parent_base:/file_parent..hash_base:/file_name,
+               # stripping nonexistent or useless pieces
+               $href .= "/" if ($params{'hash_base'} || $params{'hash_parent_base'}
+                       || $params{'hash_parent'} || $params{'hash'});
                if (defined $params{'hash_base'}) {
-                       $href .= "/".esc_url($params{'hash_base'});
-                       if (defined $params{'file_name'}) {
-                               $href .= ":".esc_url($params{'file_name'});
+                       if (defined $params{'hash_parent_base'}) {
+                               $href .= esc_url($params{'hash_parent_base'});
+                               # skip the file_parent if it's the same as the file_name
+                               delete $params{'file_parent'} if $params{'file_parent'} eq $params{'file_name'};
+                               if (defined $params{'file_parent'} && $params{'file_parent'} !~ /\.\./) {
+                                       $href .= ":/".esc_url($params{'file_parent'});
+                                       delete $params{'file_parent'};
+                               }
+                               $href .= "..";
+                               delete $params{'hash_parent'};
+                               delete $params{'hash_parent_base'};
+                       } elsif (defined $params{'hash_parent'}) {
+                               $href .= esc_url($params{'hash_parent'}). "..";
+                               delete $params{'hash_parent'};
+                       }
+
+                       $href .= esc_url($params{'hash_base'});
+                       if (defined $params{'file_name'} && $params{'file_name'} !~ /\.\./) {
+                               $href .= ":/".esc_url($params{'file_name'});
                                delete $params{'file_name'};
                        }
                        delete $params{'hash'};
                        delete $params{'hash_base'};
                } elsif (defined $params{'hash'}) {
-                       $href .= "/".esc_url($params{'hash'});
+                       $href .= esc_url($params{'hash'});
                        delete $params{'hash'};
                }
        }