gitserver

self-hosted git server tooling
git clone https://git.ryansepassi.com/git/gitserver.git
Log | Files | Refs | README

filetree-html (2250B)


      1 #!/bin/sh
      2 # Emit a collapsible <details> file tree (HTML fragment) for a bare repo's HEAD.
      3 # Used by stagit-update to replace stagit's flat <table id="files"> in files.html.
      4 # Output: a single <div id="filetree">...</div>. Directories are collapsed
      5 # <details>; files link to the same file/<path>.html blob pages stagit generates.
      6 # Usage: filetree-html <gitdir>
      7 set -eu
      8 
      9 repo=$1
     10 
     11 # Sort rows so that within each directory subdirs group before files (both
     12 # alphabetical): build a key prefixing every dir component with "0" and the file
     13 # leaf with "1", sort on it, then drop the key. Directory subtrees stay
     14 # contiguous, which the tree-builder below relies on.
     15 git --git-dir="$repo" ls-tree -r --long HEAD \
     16 | awk -F'\t' '{
     17     np = split($2, c, "/"); key = ""
     18     for (i = 1; i < np; i++) key = key "0" c[i] "/"
     19     print key "1" c[np] "\t" $0
     20 }' \
     21 | LC_ALL=C sort \
     22 | cut -f2- \
     23 | awk '
     24 function esc(s) {
     25     gsub(/&/, "\\&amp;", s); gsub(/</, "\\&lt;", s)
     26     gsub(/>/, "\\&gt;", s); gsub(/"/, "\\&quot;", s)
     27     return s
     28 }
     29 function hsize(n) {
     30     if (n == "-" || n == "") return ""
     31     if (n < 1024)       return n "B"
     32     if (n < 1048576)    return sprintf("%.1fK", n / 1024)
     33     if (n < 1073741824) return sprintf("%.1fM", n / 1048576)
     34     return sprintf("%.1fG", n / 1073741824)
     35 }
     36 BEGIN { print "<div id=\"filetree\">"; depth = 0 }
     37 {
     38     # line: <mode> <type> <sha> <size>\t<path>
     39     tab  = index($0, "\t")
     40     meta = substr($0, 1, tab - 1)
     41     path = substr($0, tab + 1)
     42     split(meta, m, /[ \t]+/); size = m[4]
     43     np = split(path, c, "/")
     44 
     45     # how many leading dir components match the currently-open dirs
     46     k = 0
     47     while (k < depth && k < np - 1 && cur[k + 1] == c[k + 1]) k++
     48 
     49     # close dirs deeper than the shared prefix
     50     while (depth > k) { print "</div></details>"; depth-- }
     51 
     52     # open dirs for the new path
     53     for (i = k + 1; i <= np - 1; i++) {
     54         print "<details><summary>" esc(c[i]) "/</summary><div class=\"tree\">"
     55         cur[i] = c[i]; depth++
     56     }
     57 
     58     # the file row
     59     print "<div class=\"f\"><a href=\"file/" esc(path) ".html\">" esc(c[np]) \
     60           "</a><span class=\"sz\">" hsize(size) "</span></div>"
     61 }
     62 END {
     63     while (depth > 0) { print "</div></details>"; depth-- }
     64     print "</div>"
     65 }
     66 '