171 lines
4.4 KiB
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)
|
|
}
|