Hi… I am well aware that this diff view is very suboptimal. It will be fixed when the refactored server comes along!
repo_commit: Properly format patchsets
package main
import (
"net/http"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/diff"
)
type usable_file_patch struct {
From diff.File
To diff.File
Chunks []diff.Chunk
}
func handle_repo_commit(w http.ResponseWriter, r *http.Request) {
data := make(map[string]any)
// TODO: Sanitize path values
group_name, repo_name, commit_id_string := r.PathValue("group_name"), r.PathValue("repo_name"), r.PathValue("commit_id")
data["group_name"], data["repo_name"], data["commit_id"] = group_name, repo_name, commit_id_string
repo, err := open_git_repo(group_name, repo_name)
if err != nil {
_, _ = w.Write([]byte("Error opening repo: " + err.Error()))
return
}
commit_id := plumbing.NewHash(commit_id_string)
commit_object, err := repo.CommitObject(commit_id)
if err != nil {
_, _ = w.Write([]byte("Error getting commit object: " + err.Error()))
return
}
data["commit_object"] = commit_object
parent_commit_object, err := commit_object.Parent(0)
if err != nil {
_, _ = w.Write([]byte("Error getting parent commit object: " + err.Error()))
return
}
data["parent_commit_object"] = parent_commit_object
patch, err := parent_commit_object.Patch(commit_object)
if err != nil {
_, _ = w.Write([]byte("Error getting patch of commit: " + err.Error()))
return
}
data["patch"] = patch
// TODO: Remove unnecessary context
usable_file_patches := make([]usable_file_patch, 0)
for _, file_patch := range patch.FilePatches() {
from, to := file_patch.Files()
usable_file_patch := usable_file_patch{
Chunks: file_patch.Chunks(),
From: from,
To: to,
}
usable_file_patches = append(usable_file_patches, usable_file_patch)
}
data["file_patches"] = usable_file_patches
err = templates.ExecuteTemplate(w, "repo_commit", data)
if err != nil {
_, _ = w.Write([]byte("Error rendering template: " + err.Error()))
return
}
}
html {
font-family: sans-serif;
background-color: var(--background-color);
color: var(--text-color);
--background-color: hsl(0, 0%, 100%);
--text-color: hsl(0, 0%, 0%);
--link-color: hsl(320, 50%, 36%);
--light-text-color: hsl(0, 0%, 45%);
--darker-border-color: hsl(0, 0%, 72%);
--lighter-border-color: hsl(0, 0%, 85%);
--text-decoration-color: hsl(0, 0%, 72%);
--darker-box-background-color: hsl(0, 0%, 92%);
--lighter-box-background-color: hsl(0, 0%, 95%);
}
@media (prefers-color-scheme: dark) {
html {
--background-color: hsl(0, 0%, 0%);
--text-color: hsl(0, 0%, 100%);
--link-color: hsl(320, 50%, 76%);
--light-text-color: hsl(0, 0%, 78%);
--darker-border-color: hsl(0, 0%, 35%);
--lighter-border-color: hsl(0, 0%, 25%);
--text-decoration-color: hsl(0, 0%, 30%);
--darker-box-background-color: hsl(0, 0%, 20%);
--lighter-box-background-color: hsl(0, 0%, 15%);
}
}
html, code, pre {
font-size: 1rem; /* TODO: Not always correct */
}
footer {
margin-top: 1rem;
margin-left: auto;
margin-right: auto;
display: block;
padding: 0 5px;
width: fit-content;
text-align: center;
color: var(--light-text-color);
}
footer a:link, footer a:visited {
color: inherit;
}
.padding-wrapper {
margin: 1rem auto;
max-width: 60rem;
padding: 0 5px;
}
a:link, a:visited {
text-decoration-color: var(--text-decoration-color);
color: var(--link-color);
}
code:not(pre > code) {
background-color: var(--lighter-box-background-color);
border-radius: 2px;
padding: 2px;
}
table {
border: var(--lighter-border-color) solid 1px;
border-spacing: 0px;
border-collapse: collapse;
}
table.wide {
width: 100%;
}
td, th {
padding: 3px 5px;
border: var(--lighter-border-color) solid 1px;
}
th {
background-color: var(--lighter-box-background-color);
}
th[scope=row] {
text-align: left;
}
tr.title-row > th {
background-color: var(--darker-box-background-color);
}
td > pre {
margin: 0;
}
td#readme > *:last-child {
margin-bottom: 0;
}
td#readme > *:first-child {
margin-top: 0;
}
.commit-id {
font-family: monospace;
}
.scroll {
overflow-x: auto;
}
.toggle-table-off, .toggle-table-on {
display: none;
}
.toggle-table-off + table > thead > tr > th, .toggle-table-on + table > thead > tr > th {
padding: 0;
}
.toggle-table-off + table > thead > tr > th > label, .toggle-table-on + table > thead > tr > th > label {
width: 100%;
display: inline-block;
padding: 3px 1px;
padding: 3px 0;
cursor: pointer;
}
.toggle-table-off:checked + table > tbody {
display: none;
}
.toggle-table-on + table > tbody {
display: none;
}
.toggle-table-on:checked + table > tbody {
display: table-row-group;
}
.chunk-unchanged {
color: grey;
}
.chunk-addition {
color: green;
}
.chunk-deletion {
color: red;
}
.chunk-unknown {
color: yellow;
}
pre.chunk {
margin-top: 0;
margin-bottom: 0;
}
.toggle-on-wrapper {
border: var(--lighter-border-color) solid 1px;
}
.toggle-on-toggle {
display: none;
}
.toggle-on-header {
cursor: pointer;
display: block;
width: 100%;
border-bottom: var(--lighter-border-color) solid 1px;
background-color: var(--lighter-box-background-color);
}
.toggle-on-content {
display: none;
}
.toggle-on-toggle:checked + .toggle-on-header + .toggle-on-content {
display: block;
}
.file-patch + .file-patch {
margin-top: 0.5rem;
}
.file-header {
font-family: monospace;
}
{{- define "repo_commit" -}}
<!DOCTYPE html>
<html lang="en">
<head>
{{ template "head_common" . }}
<title>{{ .group_name }}/repos/{{ .repo_name }} – Lindenii Forge</title>
</head>
<body class="repo-commit">
<div class="padding-wrapper scroll">
<table id="commit-info-table">
<thead>
<tr class="title-row">
<th colspan="2">Commit Info</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">ID</th>
<td>{{ .commit_id }}</td>
</tr>
<tr>
<th scope="row">Author</th>
<td>{{ .commit_object.Author.Name }} <<a href="mailto:{{ .commit_object.Author.Email }}">{{ .commit_object.Author.Email }}</a>></td>
</tr>
<tr>
<th scope="row">Author Date</th>
<td>{{ .commit_object.Author.When.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</td>
</tr>
<tr>
<th scope="row">Committer</th>
<td>{{ .commit_object.Committer.Name }} <<a href="mailto:{{ .commit_object.Committer.Email }}">{{ .commit_object.Committer.Email }}</a>></td>
</tr>
<tr>
<th scope="row">Committer Date</th>
<td>{{ .commit_object.Committer.When.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</td>
</tr>
<tr>
<th scope="row">Message</th>
<td><pre>{{ .commit_object.Message }}</pre></td>
</tr>
</tbody>
</table>
</div>
<div class="padding-wrapper scroll">
<div class="padding-wrapper">
{{ $parent_commit_object := .parent_commit_object }}
{{ $commit_object := .commit_object }}
{{ range .file_patches }}
<div class="file-patch">
{{ .From.Path }} {{ .From.Mode }} → {{ .To.Path }} {{ .To.Mode }}
{{ range .Chunks }}
{{ if eq .Type 0 }}
<pre class="chunk chunk-unchanged">{{ .Content }}</pre>
{{ else if eq .Type 1 }}
<pre class="chunk chunk-addition">{{ .Content }}</pre>
{{ else if eq .Type 2 }}
<pre class="chunk chunk-deletion">{{ .Content }}</pre>
{{ else }}
<pre class="chunk chunk-unknown">{{ .Content }}</pre>
<div class="file-patch toggle-on-wrapper">
<input type="checkbox" id="toggle-{{ .From.Hash }}{{ .To.Hash }}" class="file-toggle toggle-on-toggle">
<label for="toggle-{{ .From.Hash }}{{ .To.Hash }}" class="file-header toggle-on-header">
<span>
--- a/<a href="../../tree/{{ .From.Path }}?commit={{ $parent_commit_object.Hash }}">{{ .From.Path }}</a> {{ .From.Mode }}
<br />
+++ b/<a href="../../tree/{{ .To.Path }}?commit={{ $commit_object.Hash }}">{{ .To.Path }}</a> {{ .To.Mode }}
</span>
</label>
<div class="file-content toggle-on-content scroll">
{{ range .Chunks }}
{{ if eq .Type 0 }}
<pre class="chunk chunk-unchanged">{{ .Content }}</pre>
{{ else if eq .Type 1 }}
<pre class="chunk chunk-addition">{{ .Content }}</pre>
{{ else if eq .Type 2 }}
<pre class="chunk chunk-deletion">{{ .Content }}</pre>
{{ else }}
<pre class="chunk chunk-unknown">{{ .Content }}</pre>
{{ end }}
{{ end }}
{{ end }}
</div>
</div>
{{ end }}
</div>
<footer>
{{ template "footer" . }}
</footer>
</body>
</html>
{{- end -}}