Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gitweb: how to display markdown file in html format automatically like github

Markdown is important for documentation, it is very nice to see README.md can be automatically show in html format in github like https://github.com/twitter/bootstrap/blob/master/README.md

gitweb is written in perl script, and there is plugin for markdown in perl already.

I want to check whether there is plugin/solution to let gitweb automatically show html files for markdown format.

like image 398
Larry Cai Avatar asked Nov 30 '11 05:11

Larry Cai


1 Answers

Here are my modifications to gitweb.cgi based on the accepted answer, so that:

  • README.md in the project root is displayed (as in accepted answer)
  • Any *.md file is displayed if it is chosen as file from the tree view
  • local links among .md files in a project are preserved (i.e. (my parent)[../parent.md] would refer correctly); should work for images as well

Note that I'm using sudo apt-get install libtext-markdown-perl on Ubuntu to provide the required markdown executable.

Here are the modifications as a diff patch:

--- /usr/share/gitweb/gitweb.cgi.orig   2016-04-13 10:28:03.268872899 +0200
+++ /usr/share/gitweb/gitweb.cgi    2016-04-13 10:39:02.344875516 +0200
@@ -16,8 +16,9 @@
 use Encode;
 use Fcntl ':mode';
 use File::Find qw();
-use File::Basename qw(basename);
+use File::Basename qw(basename dirname);
 use Time::HiRes qw(gettimeofday tv_interval);
+use File::Spec; # hack
 binmode STDOUT, ':utf8';

 our $t0 = [ gettimeofday() ];
@@ -6585,6 +6586,20 @@
        print "\n</div>\n"; # class="readme"
    }

+   # hack
+   if (!$prevent_xss) {
+       $file_name = "README.md";
+       my $proj_head_hash = git_get_head_hash($project);
+       my $readme_blob_hash = git_get_hash_by_path($proj_head_hash, "README.md", "blob");
+
+       if ($readme_blob_hash) { # if README.md exists
+           print "<div class=\"header\">$file_name</div>\n";
+           print "<div class=\"readme page_body\">"; # TODO find/create a better CSS class than page_body
+           print get_markdown($file_name, $readme_blob_hash);
+           print "</div>";
+       }
+   }
+
    # we need to request one more than 16 (0..15) to check if
    # those 16 are all
    my @commitlist = $head ? parse_commits($head, 17) : ();
@@ -7059,6 +7074,9 @@
    $fd = run_highlighter($fd, $highlight, $syntax)
        if $syntax;

+   # hack
+   my $ismarkdown = ($file_name =~ /md$/);
+
    git_header_html(undef, $expires);
    my $formats_nav = '';
    if (defined $hash_base && (my %co = parse_commit($hash_base))) {
@@ -7102,6 +7120,10 @@
              href(action=>"blob_plain", hash=>$hash,
                   hash_base=>$hash_base, file_name=>$file_name) .
              qq!" />\n!;
+   } elsif ($ismarkdown) {
+       print qq!<div class="readme page_body">\n!;
+       print get_markdown($file_name, $hash);
+       print qq!</div>\n!; #  $cmd_markdownify
    } else {
        my $nr;
        while (my $line = <$fd>) {
@@ -7119,6 +7141,79 @@
    git_footer_html();
 }

+# hack
+sub get_norm_rel_path { # http://www.perlmonks.org/bare/?node_id=11907
+   my $unnormpath = shift;
+   while ($unnormpath =~ m!/\.!) {
+       $unnormpath =~ s!/[^\/]+/\.\.!!;
+       # print "Path is now -+$unnormpath+-\n";
+   }
+   return $unnormpath;
+}
+sub get_markdown {
+   my $tfilename = shift;
+   my $thash = shift;
+   my $rethtmlstr = "";
+   use open ":encoding(utf8)"; # needed to have utf8 survive through the shell pipe
+   my $cmd_markdownify = $GIT . " " . git_cmd() . " cat-file blob " . $thash . " | perl -e 'my \$str = do { local \$/; <STDIN> }; \$str =~ s/<!--.*?--\s*>//gs; print \$str;' | markdown |";
+   open (FOO, $cmd_markdownify) or die_error(500, "Open git-cat-file blob '$thash' failed");
+   while (<FOO>) {
+       if ($_ =~ /(<img[^>]src=")(.*?)"/) {
+           my $origcut = "".$2;
+           my $testcut = "".$2;
+           my $is_anchor = ($testcut =~ /^#/);
+           my $is_absolute = ($testcut =~ /^http/);
+           my $is_relative_up = ($testcut =~ /^\.\./);
+           my $is_local_link = ((!$is_anchor) and (!$is_absolute));
+           my $tdir = dirname($tfilename);
+           my $is_tdir_proper = (($tdir ne "") and ($tdir ne "."));
+           #print "XX: $origcut ($is_anchor, $is_absolute - $is_local_link) ($is_relative_up, $is_tdir_proper, $tdir, $tfilename)\n"; # dbg
+           if ($is_local_link) {
+               if ($is_relative_up) { # normalize
+                   if ($is_tdir_proper) {
+                       # cheat with absolute path here:
+                       my $resolved = get_norm_rel_path( File::Spec->rel2abs ("$origcut", "/$tdir" ) );
+                       $resolved = substr $resolved, 1;
+                       #print "YY: $resolved\n";
+                       $_ =~ s!(<img[^>]src=")(.*?)"!$1?p=$project;a=blob_plain;f=$resolved"!gi;
+                   }
+               } else {
+                   $_ =~ s!(<img[^>]src=")(.*?)"!$1?p=$project;a=blob_plain;f=$2"!gi;
+                   #print "ZZ: $_\n";
+               }
+           }
+       }
+       if ($_ =~ /(<a[^>]href=")(.*?)"/) {
+           my $origcut = "".$2;
+           my $testcut = "".$2;
+           my $is_anchor = ($testcut =~ /^#/);
+           my $is_absolute = ($testcut =~ /^http/);
+           my $is_relative_up = ($testcut =~ /^\.\./);
+           my $is_local_link = ((!$is_anchor) and (!$is_absolute));
+           my $tdir = dirname($tfilename);
+           my $is_tdir_proper = (($tdir ne "") and ($tdir ne "."));
+           #print "XX: $origcut ($is_anchor, $is_absolute - $is_local_link) ($is_relative_up, $is_tdir_proper, $tdir, $tfilename)\n"; # dbg
+           if ($is_local_link) {
+               if ($is_relative_up) { # normalize
+                   if ($is_tdir_proper) {
+                       # cheat with absolute path here:
+                       my $resolved = get_norm_rel_path( File::Spec->rel2abs ("$origcut", "/$tdir" ) );
+                       $resolved = substr $resolved, 1;
+                       #print "YY: $resolved\n";
+                       $_ =~ s!(<a[^>]href=")(.*?)"!$1?p=$project;a=blob;f=$resolved"!gi;
+                   }
+               } else {
+                   $_ =~ s!(<a[^>]href=")(.*?)"!$1?p=$project;a=blob;f=$2"!gi;
+                   #print "ZZ: $_\n";
+               }
+           }
+       }
+       $rethtmlstr .= $_;
+   }
+   close(FOO);
+   return $rethtmlstr;
+}
+
 sub git_tree {
    if (!defined $hash_base) {
        $hash_base = "HEAD";
like image 189
sdbbs Avatar answered Sep 27 '22 02:09

sdbbs