aboutsummaryrefslogtreecommitdiff
path: root/internal/unsorted/http_handle_repo_commit.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/unsorted/http_handle_repo_commit.go')
-rw-r--r--internal/unsorted/http_handle_repo_commit.go146
1 files changed, 146 insertions, 0 deletions
diff --git a/internal/unsorted/http_handle_repo_commit.go b/internal/unsorted/http_handle_repo_commit.go
new file mode 100644
index 0000000..44f8f54
--- /dev/null
+++ b/internal/unsorted/http_handle_repo_commit.go
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: AGPL-3.0-only
+// SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
+
+package unsorted
+
+import (
+ "fmt"
+ "net/http"
+ "strings"
+
+ "github.com/go-git/go-git/v5"
+ "github.com/go-git/go-git/v5/plumbing"
+ "github.com/go-git/go-git/v5/plumbing/filemode"
+ "github.com/go-git/go-git/v5/plumbing/format/diff"
+ "github.com/go-git/go-git/v5/plumbing/object"
+ "go.lindenii.runxiyu.org/forge/internal/misc"
+ "go.lindenii.runxiyu.org/forge/internal/oldgit"
+ "go.lindenii.runxiyu.org/forge/internal/web"
+)
+
+// usableFilePatch is a [diff.FilePatch] that is structured in a way more
+// friendly for use in HTML templates.
+type usableFilePatch struct {
+ From diff.File
+ To diff.File
+ Chunks []usableChunk
+}
+
+// usableChunk is a [diff.Chunk] that is structured in a way more friendly for
+// use in HTML templates.
+type usableChunk struct {
+ Operation diff.Operation
+ Content string
+}
+
+func (s *Server) httpHandleRepoCommit(writer http.ResponseWriter, request *http.Request, params map[string]any) {
+ var repo *git.Repository
+ var commitIDStrSpec, commitIDStrSpecNoSuffix string
+ var commitID plumbing.Hash
+ var parentCommitHash plumbing.Hash
+ var commitObj *object.Commit
+ var commitIDStr string
+ var err error
+ var patch *object.Patch
+
+ repo, commitIDStrSpec = params["repo"].(*git.Repository), params["commit_id"].(string)
+
+ commitIDStrSpecNoSuffix = strings.TrimSuffix(commitIDStrSpec, ".patch")
+ commitID = plumbing.NewHash(commitIDStrSpecNoSuffix)
+ if commitObj, err = repo.CommitObject(commitID); err != nil {
+ web.ErrorPage500(s.templates, writer, params, "Error getting commit object: "+err.Error())
+ return
+ }
+ if commitIDStrSpecNoSuffix != commitIDStrSpec {
+ var patchStr string
+ if patchStr, err = oldgit.FmtCommitPatch(commitObj); err != nil {
+ web.ErrorPage500(s.templates, writer, params, "Error formatting patch: "+err.Error())
+ return
+ }
+ fmt.Fprintln(writer, patchStr)
+ return
+ }
+ commitIDStr = commitObj.Hash.String()
+
+ if commitIDStr != commitIDStrSpec {
+ http.Redirect(writer, request, commitIDStr, http.StatusSeeOther)
+ return
+ }
+
+ params["commit_object"] = commitObj
+ params["commit_id"] = commitIDStr
+
+ parentCommitHash, patch, err = oldgit.CommitToPatch(commitObj)
+ if err != nil {
+ web.ErrorPage500(s.templates, writer, params, "Error getting patch from commit: "+err.Error())
+ return
+ }
+ params["parent_commit_hash"] = parentCommitHash.String()
+ params["patch"] = patch
+
+ params["file_patches"] = makeUsableFilePatches(patch)
+
+ s.renderTemplate(writer, "repo_commit", params)
+}
+
+type fakeDiffFile struct {
+ hash plumbing.Hash
+ mode filemode.FileMode
+ path string
+}
+
+func (f fakeDiffFile) Hash() plumbing.Hash {
+ return f.hash
+}
+
+func (f fakeDiffFile) Mode() filemode.FileMode {
+ return f.mode
+}
+
+func (f fakeDiffFile) Path() string {
+ return f.path
+}
+
+var nullFakeDiffFile = fakeDiffFile{ //nolint:gochecknoglobals
+ hash: plumbing.NewHash("0000000000000000000000000000000000000000"),
+ mode: misc.FirstOrPanic(filemode.New("100644")),
+ path: "",
+}
+
+func makeUsableFilePatches(patch diff.Patch) (usableFilePatches []usableFilePatch) {
+ // TODO: Remove unnecessary context
+ // TODO: Prepend "+"/"-"/" " instead of solely distinguishing based on color
+
+ for _, filePatch := range patch.FilePatches() {
+ var fromFile, toFile diff.File
+ var ufp usableFilePatch
+ chunks := []usableChunk{}
+
+ fromFile, toFile = filePatch.Files()
+ if fromFile == nil {
+ fromFile = nullFakeDiffFile
+ }
+ if toFile == nil {
+ toFile = nullFakeDiffFile
+ }
+ for _, chunk := range filePatch.Chunks() {
+ var content string
+
+ content = chunk.Content()
+ if len(content) > 0 && content[0] == '\n' {
+ content = "\n" + content
+ } // Horrible hack to fix how browsers newlines that immediately proceed <pre>
+ chunks = append(chunks, usableChunk{
+ Operation: chunk.Type(),
+ Content: content,
+ })
+ }
+ ufp = usableFilePatch{
+ Chunks: chunks,
+ From: fromFile,
+ To: toFile,
+ }
+ usableFilePatches = append(usableFilePatches, ufp)
+ }
+ return
+}