mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in NOTICE, alongside LICENSE. The header lint accepts any copyright line above the SPDX line, so it no longer demands an upstream one. Co-authored-by: Hanzo Dev <[email protected]>
100 lines
2.5 KiB
Go
100 lines
2.5 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package git
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"strings"
|
|
|
|
"github.com/hanzoai/git/modules/log"
|
|
)
|
|
|
|
// NotesRef is the git ref where the forge 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 {
|
|
Message []byte
|
|
Commit *Commit
|
|
}
|
|
|
|
// GetNote retrieves the git-notes data for a given commit.
|
|
// FIXME: Add LastCommitCache support
|
|
func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) error {
|
|
log.Trace("Searching for git note corresponding to the commit %q in the repository %q", commitID, repo.Path)
|
|
notes, err := repo.GetCommit(NotesRef)
|
|
if err != nil {
|
|
if IsErrNotExist(err) {
|
|
return err
|
|
}
|
|
log.Error("Unable to get commit from ref %q. Error: %v", NotesRef, err)
|
|
return err
|
|
}
|
|
|
|
path := ""
|
|
|
|
tree := notes.Tree()
|
|
log.Trace("Found tree with ID %q while searching for git note corresponding to the commit %q", tree.ID, commitID)
|
|
|
|
var entry *TreeEntry
|
|
originalCommitID := commitID
|
|
for len(commitID) > 2 {
|
|
entry, err = tree.GetTreeEntryByPath(ctx, repo, commitID)
|
|
if err == nil {
|
|
path += commitID
|
|
break
|
|
}
|
|
if IsErrNotExist(err) {
|
|
tree, err = tree.SubTree(ctx, repo, commitID[0:2])
|
|
path += commitID[0:2] + "/"
|
|
commitID = commitID[2:]
|
|
}
|
|
if err != nil {
|
|
// Err may have been updated by the SubTree we need to recheck if it's again an ErrNotExist
|
|
if !IsErrNotExist(err) {
|
|
log.Error("Unable to find git note corresponding to the commit %q. Error: %v", originalCommitID, err)
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
|
|
blob := entry.Blob(repo)
|
|
dataRc, err := blob.DataAsync()
|
|
if err != nil {
|
|
log.Error("Unable to read blob with ID %q. Error: %v", blob.ID, err)
|
|
return err
|
|
}
|
|
closed := false
|
|
defer func() {
|
|
if !closed {
|
|
_ = dataRc.Close()
|
|
}
|
|
}()
|
|
d, err := io.ReadAll(dataRc)
|
|
if err != nil {
|
|
log.Error("Unable to read blob with ID %q. Error: %v", blob.ID, err)
|
|
return err
|
|
}
|
|
_ = dataRc.Close()
|
|
closed = true
|
|
note.Message = d
|
|
|
|
treePath := ""
|
|
if idx := strings.LastIndex(path, "/"); idx > -1 {
|
|
treePath = path[:idx]
|
|
path = path[idx+1:]
|
|
}
|
|
|
|
lastCommits, err := GetLastCommitForPaths(ctx, repo, notes, treePath, []string{path})
|
|
if err != nil {
|
|
log.Error("Unable to get the commit for the path %q. Error: %v", treePath, err)
|
|
return err
|
|
}
|
|
note.Commit = lastCommits[path]
|
|
|
|
return nil
|
|
}
|