Lindenii Project Forge
HTTP: git log should stop on context done
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org> package unsorted import ( "context" "errors" "io" "iter" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/object" "github.com/jackc/pgx/v5/pgtype" ) // openRepo opens a git repository by group and repo name. // // TODO: This should be deprecated in favor of doing it in the relevant // request/router context in the future, as it cannot cover the nuance of // fields needed. func (s *Server) openRepo(ctx context.Context, groupPath []string, repoName string) (repo *git.Repository, description string, repoID int, fsPath string, err error) { err = s.database.QueryRow(ctx, ` WITH RECURSIVE group_path_cte AS ( -- Start: match the first name in the path where parent_group IS NULL SELECT id, parent_group, name, 1 AS depth FROM groups WHERE name = ($1::text[])[1] AND parent_group IS NULL UNION ALL -- Recurse: join next segment of the path SELECT g.id, g.parent_group, g.name, group_path_cte.depth + 1 FROM groups g JOIN group_path_cte ON g.parent_group = group_path_cte.id WHERE g.name = ($1::text[])[group_path_cte.depth + 1] AND group_path_cte.depth + 1 <= cardinality($1::text[]) ) SELECT r.filesystem_path, COALESCE(r.description, ''), r.id FROM group_path_cte g JOIN repos r ON r.group_id = g.id WHERE g.depth = cardinality($1::text[]) AND r.name = $2 `, pgtype.FlatArray[string](groupPath), repoName).Scan(&fsPath, &description, &repoID) if err != nil { return } repo, err = git.PlainOpen(fsPath) return } // commitIterSeqErr creates an [iter.Seq[*object.Commit]] from an // [object.CommitIter], and additionally returns a pointer to error. // The pointer to error is guaranteed to be populated with either nil or the // error returned by the commit iterator after the returned iterator is // finished.
func commitIterSeqErr(commitIter object.CommitIter) (iter.Seq[*object.Commit], *error) {
func commitIterSeqErr(ctx context.Context, commitIter object.CommitIter) (iter.Seq[*object.Commit], *error) {
var err error return func(yield func(*object.Commit) bool) { for { commit, err2 := commitIter.Next() if err2 != nil { if errors.Is(err2, io.EOF) { return } err = err2 return }
select { case <-ctx.Done(): err = ctx.Err() return default: }
if !yield(commit) { return } } }, &err }
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org> package unsorted import ( "net/http" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "go.lindenii.runxiyu.org/forge/forged/internal/web" ) // httpHandleRepoLog provides a page with a complete Git log. // // TODO: This currently provides all commits in the branch. It should be // paginated and cached instead.
func (s *Server) httpHandleRepoLog(writer http.ResponseWriter, _ *http.Request, params map[string]any) {
func (s *Server) httpHandleRepoLog(writer http.ResponseWriter, req *http.Request, params map[string]any) {
var repo *git.Repository var refHash plumbing.Hash var err error repo = params["repo"].(*git.Repository) if refHash, err = getRefHash(repo, params["ref_type"].(string), params["ref_name"].(string)); err != nil { web.ErrorPage500(s.templates, writer, params, "Error getting ref hash: "+err.Error()) return } logOptions := git.LogOptions{From: refHash} //exhaustruct:ignore commitIter, err := repo.Log(&logOptions) if err != nil { web.ErrorPage500(s.templates, writer, params, "Error getting recent commits: "+err.Error()) return }
params["commits"], params["commits_err"] = commitIterSeqErr(commitIter)
params["commits"], params["commits_err"] = commitIterSeqErr(req.Context(), commitIter)
s.renderTemplate(writer, "repo_log", params) }