gitea_hat/modules/git/repo_commit.go

171 lines
4.4 KiB
Go

package git
import (
"bytes"
"errors"
"io"
"strconv"
"strings"
gitea_git "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/log"
)
const prettyLogFormat = `--pretty=format:%H`
func GetFirstAndLastCommitByPath(repo *gitea_git.Repository, revision, relpath string) (*gitea_git.Commit, *gitea_git.Commit, error) {
stdout, _, runErr := gitcmd.NewCommand("log", "-1", prettyLogFormat).AddDynamicArguments(revision, prettyLogFormat, "--"+relpath).WithDir(repo.Path).RunStdBytes(repo.Ctx)
if runErr != nil {
return nil, nil, runErr
}
var commits []*gitea_git.Commit
if len(stdout) == 0 {
return nil, nil, nil
}
parts := bytes.Split(stdout, []byte{'\n'})
for _, commitID := range parts {
commit, err := repo.GetCommit(string(commitID))
if err != nil {
return nil, nil, err
}
commits = append(commits, commit)
}
return commits[0], commits[len(commits)-1], nil
}
// CommitsByFileAndRange return the commits according revision file and the page
func AllCommitsByFileAndRange(repo *gitea_git.Repository, opts gitea_git.CommitsByFileAndRangeOptions, keyword string, pageSize int) ([]*gitea_git.Commit, error) {
skip := (opts.Page - 1) * pageSize
gitCmd := gitcmd.NewCommand("rev-list").
AddOptionFormat("--max-count=%d", pageSize*opts.Page).
AddOptionFormat("--skip=%d", skip).
AddOptionFormat("--grep=%s", keyword)
gitCmd.AddDynamicArguments(opts.Revision)
if opts.Not != "" {
gitCmd.AddOptionValues("--not", opts.Not)
}
if opts.Since != "" {
gitCmd.AddOptionFormat("--since=%s", opts.Since)
}
if opts.Until != "" {
gitCmd.AddOptionFormat("--until=%s", opts.Until)
}
gitCmd.AddDashesAndList(opts.File)
var commits []*gitea_git.Commit
stdoutReader, stdoutReaderClose := gitCmd.MakeStdoutPipe()
defer stdoutReaderClose()
err := gitCmd.WithDir(repo.Path).
WithPipelineFunc(func(context gitcmd.Context) error {
objectFormat, err := repo.GetObjectFormat()
if err != nil {
return err
}
length := objectFormat.FullLength()
shaline := make([]byte, length+1)
for {
n, err := io.ReadFull(stdoutReader, shaline)
if err != nil || n < length {
if err == io.EOF {
err = nil
}
return err
}
objectID, err := gitea_git.NewIDFromString(string(shaline[0:length]))
if err != nil {
return err
}
commit, err := getCommit(repo, objectID)
if err != nil {
return err
}
commits = append(commits, commit)
}
}).
RunWithStderr(repo.Ctx)
return commits, err
}
func getCommit(repo *gitea_git.Repository, id gitea_git.ObjectID) (*gitea_git.Commit, error) {
batch, cancel, err := repo.CatFileBatch(repo.Ctx)
if err != nil {
return nil, err
}
defer cancel()
return getCommitFromBatchReader(repo, batch, id)
}
func getCommitFromBatchReader(repo *gitea_git.Repository, batch gitea_git.CatFileBatch, id gitea_git.ObjectID) (*gitea_git.Commit, error) {
info, rd, err := batch.QueryContent(id.String())
if err != nil {
if errors.Is(err, io.EOF) || gitea_git.IsErrNotExist(err) {
return nil, gitea_git.ErrNotExist{ID: id.String()}
}
return nil, err
}
switch info.Type {
case "missing":
return nil, gitea_git.ErrNotExist{ID: id.String()}
case "tag":
// then we need to parse the tag
// and load the commit
data, err := io.ReadAll(io.LimitReader(rd, info.Size))
if err != nil {
return nil, err
}
_, err = rd.Discard(1)
if err != nil {
return nil, err
}
tag, err := parseTagData(id.Type(), data)
if err != nil {
return nil, err
}
return getCommitFromBatchReader(repo, batch, tag.Object)
case "commit":
commit, err := gitea_git.CommitFromReader(repo, id, io.LimitReader(rd, info.Size))
if err != nil {
return nil, err
}
_, err = rd.Discard(1)
if err != nil {
return nil, err
}
return commit, nil
default:
log.Debug("Unknown cat-file object type: %s", info.Type)
if err := gitea_git.DiscardFull(rd, info.Size+1); err != nil {
return nil, err
}
return nil, gitea_git.ErrNotExist{
ID: id.String(),
}
}
}
func GetAllCommitsCount(repo *gitea_git.Repository, keyword string) (int64, error) {
cmd := gitcmd.NewCommand("rev-list").
AddOptionFormat("--grep=%s", keyword)
cmd.AddArguments("--exclude=" + "refs/pull/" + "*")
cmd.AddArguments("--all", "--count")
stdout, _, err := cmd.WithDir(repo.Path).RunStdString(repo.Ctx)
if err != nil {
return 0, err
}
return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
}