mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-24 20:23:10 +00:00
191287d8be
Since #39229 the commit page header dereferences `.Verification.CommittingUser` when the committer is not the author. `Verification` is `nil` for unsigned commits (see `repo.Diff`), so opening such a commit — a rebased or cherry-picked one, for example — logs a template error and the page comes out truncated: ``` Render failed: failed to render template: repo/commit_page, error: template error: builtin(bindata):repo/commit_page:138:22 : executing "repo/commit_page" at <.Verification.CommittingUser>: nil pointer evaluating interface {}.CommittingUser ``` This guards the access and adds an integration test that creates a commit with distinct author and committer identities and checks the page renders completely (the status stays 200 on a mid-render failure, so the test looks at the body). _The fix was worked out with help from an AI assistant; I reviewed and tested it myself._ --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
87 lines
2.4 KiB
Go
87 lines
2.4 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package git
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
|
|
"gitea.dev/modules/git/gitcmd"
|
|
"gitea.dev/modules/git/gitrepo"
|
|
"gitea.dev/modules/util"
|
|
)
|
|
|
|
type FastImportInit struct {
|
|
Bare bool
|
|
ObjectFormat string
|
|
}
|
|
|
|
type FastImportFile struct {
|
|
Mode EntryMode
|
|
Path string
|
|
Content string
|
|
}
|
|
|
|
type FastImportCommit struct {
|
|
Ref string
|
|
Message string
|
|
Files []FastImportFile
|
|
|
|
Author, Committer *Signature
|
|
}
|
|
|
|
// ForceFastImportWithInit is for mainly for testing purpose
|
|
func ForceFastImportWithInit(ctx context.Context, repoLocalPath string, commits []FastImportCommit, initOpts ...FastImportInit) (RepositoryFacade, error) {
|
|
repo := gitrepo.RepositoryUnmanaged(repoLocalPath)
|
|
initOpt := util.OptionalArg(initOpts, FastImportInit{Bare: true})
|
|
dirEntries, err := os.ReadDir(repoLocalPath)
|
|
if os.IsNotExist(err) || (err == nil && len(dirEntries) == 0) {
|
|
_ = os.MkdirAll(repoLocalPath, 0o755)
|
|
err := InitRepositoryLocal(ctx, repoLocalPath, initOpt.Bare, util.IfZero(initOpt.ObjectFormat, "sha1"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return repo, ForceFastImport(ctx, repo, commits)
|
|
}
|
|
|
|
// ForceFastImport is for mainly for testing purpose
|
|
func ForceFastImport(ctx context.Context, repo RepositoryFacade, commits []FastImportCommit) error {
|
|
buf := &bytes.Buffer{}
|
|
for i, c := range commits {
|
|
_, _ = fmt.Fprintf(buf, "reset %s\n", c.Ref)
|
|
_, _ = fmt.Fprintf(buf, "commit %s\n", c.Ref)
|
|
_, _ = fmt.Fprintf(buf, "mark :%d\n", i+1)
|
|
|
|
if c.Author != nil {
|
|
buf.WriteString("author ")
|
|
_ = c.Author.Encode(buf)
|
|
buf.WriteByte('\n')
|
|
}
|
|
if c.Committer != nil {
|
|
buf.WriteString("committer ")
|
|
_ = c.Committer.Encode(buf)
|
|
buf.WriteByte('\n')
|
|
} else {
|
|
// "committer" is required, so we use a default one if not provided
|
|
buf.WriteString("committer Gitea <gitea@example.com> 1500000000 +0000\n")
|
|
}
|
|
|
|
msg := util.IfZero(c.Message, fmt.Sprintf("test commit %d", i+1))
|
|
_, _ = fmt.Fprintf(buf, "data %d\n%s\n", len(msg), msg)
|
|
|
|
for _, f := range c.Files {
|
|
mode := util.IfZero(f.Mode, EntryModeBlob)
|
|
_, _ = fmt.Fprintf(buf, "M %s inline %s\ndata %d\n%s\n", mode.String(), f.Path, len(f.Content), f.Content)
|
|
}
|
|
}
|
|
buf.WriteString("done\n")
|
|
_, _, err := gitcmd.NewCommand("fast-import").AddArguments("--force", "--done").
|
|
WithRepo(repo).WithStdinBytes(buf.Bytes()).
|
|
RunStdString(ctx)
|
|
return err
|
|
}
|