Gitweb: how to display markdown file in html format automatically like github
Question
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.
Solution
Here's something you can stick somewhere under sub git_summary
in your gitweb.perl
or gitweb.cgi
. Note that it depends on an external markdown
executable.
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\">readme</div>\n";
print "<div class=\"readme page_body\">"; # TODO find/create a better CSS class than page_body
my $cmd_markdownify = $GIT . " " . git_cmd() . " cat-file blob " . $readme_blob_hash . " | markdown |";
open FOO, $cmd_markdownify or die_error(500, "Open git-cat-file blob '$hash' failed");
while (<FOO>) {
print $_;
}
close(FOO);
print "</div>";
}
}
I don't really know Perl, so this is a dirty hack more than anything else, but it works.
OTHER TIPS
I use following post-receive hook in my remote repositories which are made browsable with gitweb.
#!/bin/sh
#
# Post-receive hook script which generates README.html to git-dir from
# README.md found at the head of master branch in repository.
#
# Gitweb can read the README.html and embed it to the project summary page.
git cat-file blob HEAD:README.md | markdown > $GIT_DIR/README.html
That script is run when I push commits from my local work repositories to the remote bare ones. You could use that or something similar depending on your workflow/setup.
More information about git hooks: http://book.git-scm.com/5_git_hooks.html
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";
I use this tampermonkey script in chrome to render README.md files as html, directly in gitweb: https://gist.github.com/nemoo/ee47cd9ad2a5b4fdddfa Works nicely even if you do not have access to the gitweb server.