Some checks failed
cache-seeder / prune (push) Has been skipped
cache-seeder / frontend (push) Failing after 3s
cache-seeder / python (push) Failing after 3s
cache-seeder / gobuild (push) Failing after 4s
cache-seeder / lint (bindata, lint-backend) (push) Failing after 3s
giteabot backport / giteabot (push) Has been skipped
giteabot / giteabot (push) Has been skipped
cache-prune / prune (push) Has been skipped
release-nightly-snapcraft / build-and-publish (ubuntu-24.04) (push) Has been cancelled
release-nightly-snapcraft / build-and-publish (ubuntu-24.04-arm) (push) Has been cancelled
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-container (push) Has been cancelled
cron-renovate / cron-renovate (push) Has been skipped
90 lines
2.6 KiB
Go
90 lines
2.6 KiB
Go
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package git
|
|
|
|
import (
|
|
"context"
|
|
"path"
|
|
"strings"
|
|
|
|
"gitea.dev/modules/setting"
|
|
)
|
|
|
|
// NotesRef is the git ref where Gitea will look for git-notes data.
|
|
// The value ("refs/notes/commits") is the default ref used by git-notes.
|
|
const NotesRef = "refs/notes/commits"
|
|
|
|
// Note stores information about a note created using git-notes.
|
|
type Note struct {
|
|
refCommit *Commit
|
|
|
|
BlobMessage CommitMessage // if the blob is too large, the message will be truncated
|
|
BlobSize int64
|
|
TreePath string
|
|
}
|
|
|
|
// GetNote retrieves the git-notes data for a given commit.
|
|
func GetNote(ctx context.Context, repo *Repository, commitID string) (*Note, error) {
|
|
noteCommit, err := repo.GetCommit(ctx, NotesRef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// A note for a commit is stored in a blob in the notes commit tree, with the path being the commit ID.
|
|
// The path can be "FullCommitID" or a fanout path like "ab/cdef...." or "ab/cd/ef.....".
|
|
tree := noteCommit.Tree()
|
|
entryName := commitID
|
|
var entry *TreeEntry
|
|
var treePathBuf strings.Builder
|
|
for len(entryName) > 2 {
|
|
entry, err = tree.GetTreeEntryByPath(ctx, repo, entryName)
|
|
if err == nil {
|
|
treePathBuf.WriteString(entryName)
|
|
break
|
|
} else if IsErrNotExist(err) {
|
|
fanoutDir, fanoutName := entryName[0:2], entryName[2:]
|
|
tree, err = tree.SubTree(ctx, repo, fanoutDir)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
treePathBuf.WriteString(fanoutDir)
|
|
treePathBuf.WriteByte('/')
|
|
entryName = fanoutName
|
|
} else {
|
|
return nil, err
|
|
}
|
|
}
|
|
if entry == nil {
|
|
return nil, ErrNotExist{ID: commitID}
|
|
}
|
|
|
|
treePath := treePathBuf.String()
|
|
blob := entry.Blob(repo)
|
|
note := &Note{TreePath: treePath, refCommit: noteCommit}
|
|
note.BlobMessage.MessageRaw, err = blob.GetBlobContent(ctx, setting.UI.MaxDisplayFileSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
note.BlobSize = blob.Size(ctx) // it should be called after the get blob content, then the "size" is cached
|
|
return note, nil
|
|
}
|
|
|
|
func GetNoteWithLastCommit(ctx context.Context, repo *Repository, commitID string) (*Note, *Commit, error) {
|
|
note, err := GetNote(ctx, repo, commitID)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
parentPath, entryName := path.Split(note.TreePath)
|
|
parentPath = strings.Trim(parentPath, "/")
|
|
lastCommits, err := GetLastCommitForPaths(ctx, repo, note.refCommit, parentPath, []string{entryName})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
lastCommit := lastCommits[entryName]
|
|
if lastCommit == nil {
|
|
return nil, nil, ErrNotExist{ID: commitID}
|
|
}
|
|
return note, lastCommit, nil
|
|
}
|