1Static git page generator.
2
3It generates static HTML pages for a git repository.
4
5## Original Repo
6The original stagit repo can be found at https://git.codemadness.org/stagit/
7
8## Changes
9This is my personal fork of stagit.
10It has been modified to fit my needs and some refs points
11to cli apps on my web-server. Feel free to reach out and ask for help if
12you are stuck on some change.
13
14* Added functionality to view raw files
15* Preserves the structure of directories in the files view
16* Syntax highlighting using [chroma](https://github.com/alecthomas/chroma) (both pygments and linguist were slow and had lexer issues)
17* The index page now links to the README.md file - hard dependency that the file needs to exist in the repo and be a valid .md file
18* [md4c](https://github.com/mity/md4c) has been added to parse markdown files
19* Add classes and ids for better style targetting
20* Draws elements (like the sidebar for example) to match the hugo site on the main domain
21* Styling changes to improve "responsive-ness" and add personalized css
22
23Changes such as adding md4c and chroma were done while trying to preserve the speed of generating the static files.
24
25The link to download the file points to `/raw/?repository=&file=`
26where file is the full path to the file relative to the repository.
27It is also possible to specify which branch to fetch the file from.
28
29Make necessary changes to the web-server to serve the file
30
31To view a file in a bare repository:
32
33 $cd path/to/repo
34 $git show HEAD:path/to/file
35
36Simple python cgi to serve the file:
37
38```
39#!/bin/python3
40
41import cgi
42import subprocess
43import mimetypes
44import sys
45
46def getFile(repo, filepath, branch):
47 gitcmd = subprocess.run(['git', 'show', f'{branch}:{filepath}'],
48 cwd=f'/home/pi/repositories/{repo}',
49 capture_output=True)
50 return gitcmd.stdout
51
52
53def guessType(filepath, extensions_map):
54 ext = '.' + filepath.split('.')[-1]
55 if ext in extensions_map:
56 return extensions_map[ext]
57 ext = ext.lower()
58 if ext in extensions_map:
59 return extensions_map[ext]
60 return extensions_map['']
61
62
63if not mimetypes.inited:
64 mimetypes.init()
65extensions_map = mimetypes.types_map.copy()
66extensions_map[''] = 'application/octet-stream'
67
68form = cgi.FieldStorage()
69repo = form.getvalue('repository')
70filepath = form.getvalue('file')
71if (form.getvalue('branch')):
72 branch = form.getvalue('branch')
73else:
74 branch = 'HEAD'
75
76ctype = guessType(filepath, extensions_map)
77response = getFile(repo, filepath, branch)
78
79if response:
80 print(f"Content-Type: {ctype}")
81 print(f"Conent-Length: {str(len(response))}")
82 print()
83 sys.stdout.flush()
84 sys.stdout.buffer.write(response)
85else:
86 print("Content-Type: text/plain")
87 print()
88 print("404: File not found")
89```
90
91### md4c
92md4c is used to parse and convert markdown documents into html. If a file
93ends with `.md` it will be parsed by md4c rather than the regular way
94stagit parses plain text files.
95
96https://github.com/mity/md4c
97
98## Usage
99
100Make files per repository:
101
102 $ mkdir -p htmldir && cd htmldir
103 $ stagit path-to-repo
104
105Make index file for repositories:
106
107 $ stagit-index repodir1 repodir2 repodir3 > index.html
108
109
110## Build and install
111
112 $ make
113 # make install
114
115
116## Dependencies
117
118- C compiler (C99).
119- libc (tested with OpenBSD, FreeBSD, NetBSD, Linux: glibc and musl).
120- libgit2 (v0.22+).
121- POSIX make (optional).
122- md4c (markdown parsing to html)
123
124## Documentation
125
126See man pages: stagit(1) and stagit-index(1).
127
128
129### Building a static binary
130
131It may be useful to build static binaries, for example to run in a chroot.
132
133It can be done like this at the time of writing (v0.24):
134
135 cd libgit2-src
136
137 # change the options in the CMake file: CMakeLists.txt
138 BUILD_SHARED_LIBS to OFF (static)
139 CURL to OFF (not needed)
140 USE_SSH OFF (not needed)
141 THREADSAFE OFF (not needed)
142 USE_OPENSSL OFF (not needed, use builtin)
143
144 mkdir -p build && cd build
145 cmake ../
146 make
147 make install
148
149
150### Extract owner field from git config
151
152A way to extract the gitweb owner for example in the format:
153
154 [gitweb]
155 owner = Name here
156
157Script:
158
159 #!/bin/sh
160 awk '/^[ ]*owner[ ]=/ {
161 sub(/^[^=]*=[ ]*/, "");
162 print $0;
163 }'
164
165
166### Set clone url for a directory of repos
167
168 #!/bin/sh
169 cd "$dir"
170 for i in *; do
171 test -d "$i" && echo "git://git.codemadness.org/$i" > "$i/url"
172 done
173
174
175### Update files on git push
176
177Using a post-receive hook the static files can be automatically updated.
178Keep in mind git push -f can change the history and the commits may need
179to be recreated. This is because stagit checks if a commit file already
180exists. It also has a cache (-c) option which can conflict with the new
181history. See stagit(1).
182
183git post-receive hook (repo/.git/hooks/post-receive):
184
185 #!/bin/sh
186 # detect git push -f
187 force=0
188 while read -r old new ref; do
189 hasrevs=$(git rev-list "$old" "^$new" | sed 1q)
190 if test -n "$hasrevs"; then
191 force=1
192 break
193 fi
194 done
195
196 # remove commits and .cache on git push -f
197 #if test "$force" = "1"; then
198 # ...
199 #fi
200
201 # see example_create.sh for normal creation of the files.
202
203
204### Create .tar.gz archives by tag
205
206 #!/bin/sh
207 name="stagit"
208 mkdir -p archives
209 git tag -l | while read -r t; do
210 f="archives/${name}-$(echo "${t}" | tr '/' '_').tar.gz"
211 test -f "${f}" && continue
212 git archive \
213 --format tar.gz \
214 --prefix "${t}/" \
215 -o "${f}" \
216 -- \
217 "${t}"
218 done
219
220
221## Features
222
223- Log of all commits from HEAD.
224- Log and diffstat per commit.
225- Show file tree with linkable line numbers.
226- Show references: local branches and tags.
227- Detect README and LICENSE file from HEAD and link it as a webpage.
228- Detect submodules (.gitmodules file) from HEAD and link it as a webpage.
229- Atom feed log (atom.xml).
230- Make index page for multiple repositories with stagit-index.
231- After generating the pages (relatively slow) serving the files is very fast,
232 simple and requires little resources (because the content is static), only
233 a HTTP file server is required.
234- Usable with text-browsers such as dillo, links, lynx and w3m.
235
236
237## Cons
238
239- Not suitable for large repositories (2000+ commits), because diffstats are
240an expensive operation, the cache (-c flag) is a workaround for this in
241some cases.
242- Not suitable for large repositories with many files, because all files
243are written for each execution of stagit. This is because stagit shows the
244lines of textfiles and there is no "cache" for file metadata
245(this would add more complexity to the code).
246- Not suitable for repositories with many branches, a quite linear
247history is assumed (from HEAD).
248
249 In these cases it is better to just use cgit or possibly change stagit to
250 run as a CGI program.
251
252- Relatively slow to run the first time (about 3 seconds for sbase,
2531500+ commits), incremental updates are faster.
254- Does not support some of the dynamic features cgit has, like:
255 - Snapshot tarballs per commit.
256 - File tree per commit.
257 - History log of branches diverged from HEAD.
258 - Stats (git shortlog -s).
259
260 This is by design, just use git locally.
261- After adding md4c for markdown rendering the stagit-index links are pointed
262 to /file/README.md.html instead of log.html this in turn requires every repo
263 to contain a README file in md. (Not really an issue for my personal use case).
264