gitness/git/commit.go

239 lines
5.8 KiB
Go

// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package git
import (
"context"
"fmt"
"time"
"github.com/harness/gitness/errors"
"github.com/harness/gitness/git/types"
)
type GetCommitParams struct {
ReadParams
// SHA is the git commit sha
SHA string
}
type Commit struct {
SHA string `json:"sha"`
Title string `json:"title"`
Message string `json:"message,omitempty"`
Author Signature `json:"author"`
Committer Signature `json:"committer"`
}
type GetCommitOutput struct {
Commit Commit `json:"commit"`
}
type Signature struct {
Identity Identity `json:"identity"`
When time.Time `json:"when"`
}
type Identity struct {
Name string `json:"name"`
Email string `json:"email"`
}
func (i *Identity) Validate() error {
if i.Name == "" {
return errors.InvalidArgument("identity name is mandatory")
}
if i.Email == "" {
return errors.InvalidArgument("identity email is mandatory")
}
return nil
}
func (s *Service) GetCommit(ctx context.Context, params *GetCommitParams) (*GetCommitOutput, error) {
if params == nil {
return nil, ErrNoParamsProvided
}
if !isValidGitSHA(params.SHA) {
return nil, errors.InvalidArgument("the provided commit sha '%s' is of invalid format.", params.SHA)
}
repoPath := getFullPathForRepo(s.reposRoot, params.RepoUID)
result, err := s.adapter.GetCommit(ctx, repoPath, params.SHA)
if err != nil {
return nil, err
}
commit, err := mapCommit(result)
if err != nil {
return nil, fmt.Errorf("failed to map rpc commit: %w", err)
}
return &GetCommitOutput{
Commit: *commit,
}, nil
}
type ListCommitsParams struct {
ReadParams
// GitREF is a git reference (branch / tag / commit SHA)
GitREF string
// After is a git reference (branch / tag / commit SHA)
// If provided, commits only up to that reference will be returned (exlusive)
After string
Page int32
Limit int32
Path string
// Since allows to filter for commits since the provided UNIX timestamp - Optional, ignored if value is 0.
Since int64
// Until allows to filter for commits until the provided UNIX timestamp - Optional, ignored if value is 0.
Until int64
// Committer allows to filter for commits based on the committer - Optional, ignored if string is empty.
Committer string
}
type RenameDetails struct {
OldPath string
NewPath string
CommitShaBefore string
CommitShaAfter string
}
type ListCommitsOutput struct {
Commits []Commit
RenameDetails []*RenameDetails
TotalCommits int
}
func (s *Service) ListCommits(ctx context.Context, params *ListCommitsParams) (*ListCommitsOutput, error) {
if params == nil {
return nil, ErrNoParamsProvided
}
repoPath := getFullPathForRepo(s.reposRoot, params.RepoUID)
gitCommits, renameDetails, err := s.adapter.ListCommits(
ctx,
repoPath,
params.GitREF,
int(params.Page),
int(params.Limit),
types.CommitFilter{
AfterRef: params.After,
Path: params.Path,
Since: params.Since,
Until: params.Until,
Committer: params.Committer,
},
)
if err != nil {
return nil, err
}
// try to get total commits between gitref and After refs
totalCommits := 0
if params.Page == 1 && len(gitCommits) < int(params.Limit) {
totalCommits = len(gitCommits)
} else if params.After != "" && params.GitREF != params.After {
div, err := s.adapter.GetCommitDivergences(ctx, repoPath, []types.CommitDivergenceRequest{
{From: params.GitREF, To: params.After},
}, 0)
if err != nil {
return nil, err
}
if len(div) > 0 {
totalCommits = int(div[0].Ahead)
}
}
commits := make([]Commit, len(gitCommits))
for i := range gitCommits {
commit, err := mapCommit(&gitCommits[i])
if err != nil {
return nil, fmt.Errorf("failed to map rpc commit: %w", err)
}
commits[i] = *commit
}
return &ListCommitsOutput{
Commits: commits,
RenameDetails: mapRenameDetails(renameDetails),
TotalCommits: totalCommits,
}, nil
}
type GetCommitDivergencesParams struct {
ReadParams
MaxCount int32
Requests []CommitDivergenceRequest
}
type GetCommitDivergencesOutput struct {
Divergences []types.CommitDivergence
}
// CommitDivergenceRequest contains the refs for which the converging commits should be counted.
type CommitDivergenceRequest struct {
// From is the ref from which the counting of the diverging commits starts.
From string
// To is the ref at which the counting of the diverging commits ends.
To string
}
// CommitDivergence contains the information of the count of converging commits between two refs.
type CommitDivergence struct {
// Ahead is the count of commits the 'From' ref is ahead of the 'To' ref.
Ahead int32
// Behind is the count of commits the 'From' ref is behind the 'To' ref.
Behind int32
}
func (s *Service) GetCommitDivergences(
ctx context.Context,
params *GetCommitDivergencesParams,
) (*GetCommitDivergencesOutput, error) {
if params == nil {
return nil, ErrNoParamsProvided
}
repoPath := getFullPathForRepo(s.reposRoot, params.RepoUID)
requests := make([]types.CommitDivergenceRequest, len(params.Requests))
for i, req := range params.Requests {
requests[i] = types.CommitDivergenceRequest{
From: req.From,
To: req.To,
}
}
// call gitea
divergences, err := s.adapter.GetCommitDivergences(
ctx,
repoPath,
requests,
params.MaxCount,
)
if err != nil {
return nil, err
}
return &GetCommitDivergencesOutput{
Divergences: divergences,
}, nil
}