[MERGE] Enhance PR Merge (Check) Fields. (#342)

This commit is contained in:
Johannes Batzill 2023-02-14 20:18:10 -08:00 committed by GitHub
parent 278ef7a496
commit 9d894c79cc
28 changed files with 301 additions and 235 deletions

View File

@ -13,3 +13,4 @@ var ErrAlreadyExists = errors.New("already exists")
var ErrInvalidArgument = errors.New("invalid argument") var ErrInvalidArgument = errors.New("invalid argument")
var ErrNotFound = errors.New("not found") var ErrNotFound = errors.New("not found")
var ErrPreconditionFailed = errors.New("precondition failed") var ErrPreconditionFailed = errors.New("precondition failed")
var ErrNotMergeable = errors.New("not mergeable")

View File

@ -295,7 +295,11 @@ func (g Adapter) GetMergeBase(ctx context.Context, repoPath, remote, base, head
} }
stdout, _, err := git.NewCommand(ctx, "merge-base", "--", base, head).RunStdString(&git.RunOpts{Dir: repoPath}) stdout, _, err := git.NewCommand(ctx, "merge-base", "--", base, head).RunStdString(&git.RunOpts{Dir: repoPath})
return strings.TrimSpace(stdout), base, err if err != nil {
return "", "", processGiteaErrorf(err, "failed to get merge-base")
}
return strings.TrimSpace(stdout), base, nil
} }
// giteaRunStdError is an implementation of the RunStdError interface in the gitea codebase. // giteaRunStdError is an implementation of the RunStdError interface in the gitea codebase.

View File

@ -33,9 +33,9 @@ func processGitErrorf(err error, format string, args ...interface{}) error {
case errors.Is(err, types.ErrInvalidArgument): case errors.Is(err, types.ErrInvalidArgument):
return status.Errorf(codes.InvalidArgument, message) return status.Errorf(codes.InvalidArgument, message)
case types.IsMergeConflictsError(err): case types.IsMergeConflictsError(err):
return status.Errorf(codes.FailedPrecondition, message) return status.Errorf(codes.Aborted, message)
case types.IsMergeUnrelatedHistoriesError(err): case types.IsMergeUnrelatedHistoriesError(err):
return status.Errorf(codes.FailedPrecondition, message) return status.Errorf(codes.Aborted, message)
default: default:
return status.Errorf(codes.Unknown, message) return status.Errorf(codes.Unknown, message)
} }

View File

@ -73,9 +73,20 @@ func (s MergeService) Merge(
// no error check needed, all branches were created when creating the temporary repo // no error check needed, all branches were created when creating the temporary repo
baseBranch := "base" baseBranch := "base"
trackingBranch := "tracking" trackingBranch := "tracking"
baseCommitSHA, _, _ := s.adapter.GetMergeBase(ctx, tmpBasePath, "origin", baseBranch, trackingBranch) headCommit, err := s.adapter.GetCommit(ctx, tmpBasePath, trackingBranch)
headCommit, _ := s.adapter.GetCommit(ctx, tmpBasePath, trackingBranch) if err != nil {
return nil, fmt.Errorf("failed to get commit of tracking branch (head): %w", err)
}
headCommitSHA := headCommit.SHA headCommitSHA := headCommit.SHA
baseCommit, err := s.adapter.GetCommit(ctx, tmpBasePath, baseBranch)
if err != nil {
return nil, fmt.Errorf("failed to get commit of base branch: %w", err)
}
baseCommitSHA := baseCommit.SHA
mergeBaseCommitSHA, _, err := s.adapter.GetMergeBase(ctx, tmpBasePath, "origin", baseBranch, trackingBranch)
if err != nil {
return nil, fmt.Errorf("failed to get merge base: %w", err)
}
if request.HeadExpectedSha != "" && request.HeadExpectedSha != headCommitSHA { if request.HeadExpectedSha != "" && request.HeadExpectedSha != headCommitSHA {
return nil, status.Errorf( return nil, status.Errorf(
@ -170,9 +181,10 @@ func (s MergeService) Merge(
refType := enum.RefFromRPC(request.RefType) refType := enum.RefFromRPC(request.RefType)
if refType == enum.RefTypeUndefined { if refType == enum.RefTypeUndefined {
return &rpc.MergeResponse{ return &rpc.MergeResponse{
MergeSha: mergeCommitSHA, BaseSha: baseCommitSHA,
BaseSha: baseCommitSHA, HeadSha: headCommitSHA,
HeadSha: headCommitSHA, MergeBaseSha: mergeBaseCommitSHA,
MergeSha: mergeCommitSHA,
}, nil }, nil
} }
@ -193,9 +205,10 @@ func (s MergeService) Merge(
} }
return &rpc.MergeResponse{ return &rpc.MergeResponse{
MergeSha: mergeCommitSHA, BaseSha: baseCommitSHA,
BaseSha: baseCommitSHA, HeadSha: headCommitSHA,
HeadSha: headCommitSHA, MergeBaseSha: mergeBaseCommitSHA,
MergeSha: mergeCommitSHA,
}, nil }, nil
} }

View File

@ -39,6 +39,9 @@ func processRPCErrorf(err error, format string, args ...interface{}) error {
return ErrInvalidArgument return ErrInvalidArgument
case rpcErr.Code() == codes.FailedPrecondition: case rpcErr.Code() == codes.FailedPrecondition:
return ErrPreconditionFailed return ErrPreconditionFailed
case rpcErr.Code() == codes.Aborted:
// TODO: this should not be so generic ...
return ErrNotMergeable
default: default:
return fallbackErr return fallbackErr
} }

View File

@ -41,9 +41,14 @@ type MergeParams struct {
// MergeOutput is result object from merging and returns // MergeOutput is result object from merging and returns
// base, head and commit sha. // base, head and commit sha.
type MergeOutput struct { type MergeOutput struct {
MergedSHA string // BaseSHA is the sha of the latest commit on the base branch that was used for merging.
BaseSHA string BaseSHA string
HeadSHA string // HeadSHA is the sha of the latest commit on the head branch that was used for merging.
HeadSHA string
// MergeBaseSHA is the sha of the merge base of the HeadSHA and BaseSHA
MergeBaseSHA string
// MergeSHA is the sha of the commit after merging HeadSHA with BaseSHA.
MergeSHA string
} }
// Merge method executes git merge operation. Refs can be sha, branch or tag. // Merge method executes git merge operation. Refs can be sha, branch or tag.
@ -83,8 +88,9 @@ func (c *Client) Merge(ctx context.Context, params *MergeParams) (MergeOutput, e
} }
return MergeOutput{ return MergeOutput{
MergedSHA: resp.GetMergeSha(), BaseSHA: resp.GetBaseSha(),
BaseSHA: resp.GetBaseSha(), HeadSHA: resp.GetHeadSha(),
HeadSHA: resp.GetHeadSha(), MergeBaseSHA: resp.GetMergeBaseSha(),
MergeSHA: resp.GetMergeSha(),
}, nil }, nil
} }

View File

@ -46,8 +46,12 @@ message MergeRequest {
} }
message MergeResponse { message MergeResponse {
// The merge_sha is merge commit between head_sha and base_sha // base_sha is the sha of the latest commit on the base branch that was used for merging.
string merge_sha = 1; string base_sha = 1;
string base_sha = 2; // head_sha is the sha of the latest commit on the head branch that was used for merging.
string head_sha = 3; string head_sha = 2;
// merge_base_sha is the sha of the merge base of the head_sha and base_sha
string merge_base_sha = 3;
// merge_sha is the sha of the commit after merging head_sha with base_sha.
string merge_sha = 4;
} }

View File

@ -175,10 +175,14 @@ type MergeResponse struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
// The merge_sha is merge commit between head_sha and base_sha // base_sha is the sha of the latest commit on the base branch that was used for merging.
MergeSha string `protobuf:"bytes,1,opt,name=merge_sha,json=mergeSha,proto3" json:"merge_sha,omitempty"` BaseSha string `protobuf:"bytes,1,opt,name=base_sha,json=baseSha,proto3" json:"base_sha,omitempty"`
BaseSha string `protobuf:"bytes,2,opt,name=base_sha,json=baseSha,proto3" json:"base_sha,omitempty"` // head_sha is the sha of the latest commit on the head branch that was used for merging.
HeadSha string `protobuf:"bytes,3,opt,name=head_sha,json=headSha,proto3" json:"head_sha,omitempty"` HeadSha string `protobuf:"bytes,2,opt,name=head_sha,json=headSha,proto3" json:"head_sha,omitempty"`
// merge_base_sha is the sha of the merge base of the head_sha and base_sha
MergeBaseSha string `protobuf:"bytes,3,opt,name=merge_base_sha,json=mergeBaseSha,proto3" json:"merge_base_sha,omitempty"`
// merge_sha is the sha of the commit after merging head_sha with base_sha.
MergeSha string `protobuf:"bytes,4,opt,name=merge_sha,json=mergeSha,proto3" json:"merge_sha,omitempty"`
} }
func (x *MergeResponse) Reset() { func (x *MergeResponse) Reset() {
@ -213,13 +217,6 @@ func (*MergeResponse) Descriptor() ([]byte, []int) {
return file_merge_proto_rawDescGZIP(), []int{1} return file_merge_proto_rawDescGZIP(), []int{1}
} }
func (x *MergeResponse) GetMergeSha() string {
if x != nil {
return x.MergeSha
}
return ""
}
func (x *MergeResponse) GetBaseSha() string { func (x *MergeResponse) GetBaseSha() string {
if x != nil { if x != nil {
return x.BaseSha return x.BaseSha
@ -234,6 +231,20 @@ func (x *MergeResponse) GetHeadSha() string {
return "" return ""
} }
func (x *MergeResponse) GetMergeBaseSha() string {
if x != nil {
return x.MergeBaseSha
}
return ""
}
func (x *MergeResponse) GetMergeSha() string {
if x != nil {
return x.MergeSha
}
return ""
}
var File_merge_proto protoreflect.FileDescriptor var File_merge_proto protoreflect.FileDescriptor
var file_merge_proto_rawDesc = []byte{ var file_merge_proto_rawDesc = []byte{
@ -266,20 +277,22 @@ var file_merge_proto_rawDesc = []byte{
0x6f, 0x72, 0x63, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x68, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x68,
0x65, 0x61, 0x64, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x65, 0x61, 0x64, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08,
0x52, 0x10, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x42, 0x72, 0x61, 0x6e, 0x52, 0x10, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x42, 0x72, 0x61, 0x6e,
0x63, 0x68, 0x22, 0x62, 0x0a, 0x0d, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x63, 0x68, 0x22, 0x88, 0x01, 0x0a, 0x0d, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70,
0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x68, 0x61,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x53, 0x68, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x53, 0x68, 0x61, 0x12,
0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x02, 0x20, 0x01, 0x19, 0x0a, 0x08, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28,
0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x53, 0x68, 0x61, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x09, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x53, 0x68, 0x61, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x65,
0x65, 0x61, 0x64, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x68, 0x72, 0x67, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x03, 0x20, 0x01,
0x65, 0x61, 0x64, 0x53, 0x68, 0x61, 0x32, 0x40, 0x0a, 0x0c, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x53, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x42, 0x61, 0x73, 0x65, 0x53, 0x68, 0x61,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x12, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x04, 0x20,
0x11, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x53, 0x68, 0x61, 0x32, 0x40, 0x0a,
0x73, 0x74, 0x1a, 0x12, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x52, 0x65, 0x0c, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x30, 0x0a,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x05, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x12, 0x11, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x65, 0x72,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x72, 0x6e, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x72, 0x70, 0x63, 0x2e,
0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x70, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42,
0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61,
0x72, 0x6e, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x69,
0x74, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

View File

@ -23,8 +23,8 @@ import (
) )
type MergeInput struct { type MergeInput struct {
Method enum.MergeMethod `json:"method"` Method enum.MergeMethod `json:"method"`
HeadSHA string `json:"head_sha"` SourceSHA string `json:"source_sha"`
} }
// Merge merges the pull request. // Merge merges the pull request.
@ -116,23 +116,26 @@ func (c *Controller) Merge(
Author: rpcIdentityFromPrincipal(session.Principal), Author: rpcIdentityFromPrincipal(session.Principal),
RefType: gitrpcenum.RefTypeBranch, RefType: gitrpcenum.RefTypeBranch,
RefName: pr.TargetBranch, RefName: pr.TargetBranch,
HeadExpectedSHA: in.HeadSHA, HeadExpectedSHA: in.SourceSHA,
}) })
if err != nil { if err != nil {
return types.MergeResponse{}, err return types.MergeResponse{}, err
} }
pr, err = c.pullreqStore.UpdateOptLock(ctx, pr, func(pr *types.PullReq) error { pr, err = c.pullreqStore.UpdateOptLock(ctx, pr, func(pr *types.PullReq) error {
now := time.Now().UnixMilli()
pr.MergeStrategy = &in.Method
pr.Merged = &now
pr.MergedBy = &session.Principal.ID
pr.State = enum.PullReqStateMerged pr.State = enum.PullReqStateMerged
// update all SHAs (might be empty if previous merge check failed) now := time.Now().UnixMilli()
pr.MergeRefSHA = &mergeOutput.MergedSHA pr.Merged = &now
pr.MergeBaseSHA = &mergeOutput.BaseSHA pr.MergedBy = &session.Principal.ID
pr.MergeHeadSHA = &mergeOutput.HeadSHA pr.MergeMethod = &in.Method
// update all Merge specific information (might be empty if previous merge check failed)
pr.MergeCheckStatus = enum.MergeCheckStatusMergeable
pr.MergeTargetSHA = &mergeOutput.BaseSHA
pr.MergeBaseSHA = &mergeOutput.MergeBaseSHA
pr.MergeSHA = &mergeOutput.MergeSHA
pr.MergeConflicts = nil
pr.ActivitySeq++ // because we need to write the activity entry pr.ActivitySeq++ // because we need to write the activity entry
return nil return nil
@ -143,9 +146,9 @@ func (c *Controller) Merge(
activityPayload := &types.PullRequestActivityPayloadMerge{ activityPayload := &types.PullRequestActivityPayloadMerge{
MergeMethod: in.Method, MergeMethod: in.Method,
MergedSHA: mergeOutput.MergedSHA, MergeSHA: mergeOutput.MergeSHA,
BaseSHA: mergeOutput.BaseSHA, TargetSHA: mergeOutput.BaseSHA,
HeadSHA: mergeOutput.HeadSHA, SourceSHA: mergeOutput.HeadSHA,
} }
if _, errAct := c.activityStore.CreateWithPayload(ctx, pr, session.Principal.ID, activityPayload); errAct != nil { if _, errAct := c.activityStore.CreateWithPayload(ctx, pr, session.Principal.ID, activityPayload); errAct != nil {
// non-critical error // non-critical error
@ -155,9 +158,9 @@ func (c *Controller) Merge(
c.eventReporter.Merged(ctx, &pullreqevents.MergedPayload{ c.eventReporter.Merged(ctx, &pullreqevents.MergedPayload{
Base: eventBase(pr, &session.Principal), Base: eventBase(pr, &session.Principal),
MergeMethod: in.Method, MergeMethod: in.Method,
MergedSHA: mergeOutput.MergedSHA, MergeSHA: mergeOutput.MergeSHA,
BaseSHA: mergeOutput.BaseSHA, TargetSHA: mergeOutput.BaseSHA,
HeadSHA: mergeOutput.HeadSHA, SourceSHA: mergeOutput.HeadSHA,
}) })
return types.MergeResponse{ return types.MergeResponse{

View File

@ -34,9 +34,11 @@ func (c *Controller) Commits(
} }
gitRef := pr.SourceBranch gitRef := pr.SourceBranch
if pr.SourceSHA != "" {
gitRef = pr.SourceSHA
}
afterRef := pr.TargetBranch afterRef := pr.TargetBranch
if pr.State == enum.PullReqStateMerged { if pr.State == enum.PullReqStateMerged {
gitRef = *pr.MergeHeadSHA
afterRef = *pr.MergeBaseSHA afterRef = *pr.MergeBaseSHA
} }

View File

@ -107,27 +107,28 @@ func newPullReq(
) *types.PullReq { ) *types.PullReq {
now := time.Now().UnixMilli() now := time.Now().UnixMilli()
return &types.PullReq{ return &types.PullReq{
ID: 0, // the ID will be populated in the data layer ID: 0, // the ID will be populated in the data layer
Version: 0, Version: 0,
Number: number, Number: number,
CreatedBy: session.Principal.ID, CreatedBy: session.Principal.ID,
Created: now, Created: now,
Updated: now, Updated: now,
Edited: now, Edited: now,
State: enum.PullReqStateOpen, State: enum.PullReqStateOpen,
IsDraft: in.IsDraft, IsDraft: in.IsDraft,
Title: in.Title, Title: in.Title,
Description: in.Description, Description: in.Description,
SourceRepoID: sourceRepo.ID, SourceRepoID: sourceRepo.ID,
SourceBranch: in.SourceBranch, SourceBranch: in.SourceBranch,
SourceSHA: sourceSHA, SourceSHA: sourceSHA,
TargetRepoID: targetRepo.ID, TargetRepoID: targetRepo.ID,
TargetBranch: in.TargetBranch, TargetBranch: in.TargetBranch,
ActivitySeq: 0, ActivitySeq: 0,
MergedBy: nil, MergedBy: nil,
Merged: nil, Merged: nil,
MergeStrategy: nil, MergeCheckStatus: enum.MergeCheckStatusUnchecked,
Author: *session.Principal.ToPrincipalInfo(), MergeMethod: nil,
Merger: nil, Author: *session.Principal.ToPrincipalInfo(),
Merger: nil,
} }
} }

View File

@ -33,14 +33,12 @@ func (c *Controller) RawDiff(
} }
headRef := pr.SourceBranch headRef := pr.SourceBranch
if pr.SourceSHA != "" {
headRef = pr.SourceSHA
}
baseRef := pr.TargetBranch baseRef := pr.TargetBranch
if pr.State == enum.PullReqStateMerged { if pr.State == enum.PullReqStateMerged {
if pr.MergeBaseSHA != nil { baseRef = *pr.MergeBaseSHA
baseRef = *pr.MergeBaseSHA
}
if pr.MergeHeadSHA != nil {
headRef = *pr.MergeHeadSHA
}
} }
return c.gitRPCClient.RawDiff(ctx, &gitrpc.DiffParams{ return c.gitRPCClient.RawDiff(ctx, &gitrpc.DiffParams{

View File

@ -7,7 +7,6 @@ package pullreq
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"github.com/harness/gitness/gitrpc" "github.com/harness/gitness/gitrpc"
"github.com/harness/gitness/internal/api/usererror" "github.com/harness/gitness/internal/api/usererror"
@ -37,14 +36,14 @@ func (c *Controller) Find(
return nil, err return nil, err
} }
headRef := pr.SourceBranch
if pr.SourceSHA != "" {
headRef = pr.SourceSHA
}
baseRef := pr.TargetBranch baseRef := pr.TargetBranch
headRef := pr.SourceSHA
if pr.MergeBaseSHA != nil { if pr.MergeBaseSHA != nil {
baseRef = *pr.MergeBaseSHA baseRef = *pr.MergeBaseSHA
} }
if pr.MergeHeadSHA != nil {
headRef = *pr.MergeHeadSHA
}
output, err := c.gitRPCClient.DiffStats(ctx, &gitrpc.DiffParams{ output, err := c.gitRPCClient.DiffStats(ctx, &gitrpc.DiffParams{
ReadParams: gitrpc.CreateRPCReadParams(repo), ReadParams: gitrpc.CreateRPCReadParams(repo),
@ -58,27 +57,5 @@ func (c *Controller) Find(
pr.Stats.DiffStats.Commits = output.Commits pr.Stats.DiffStats.Commits = output.Commits
pr.Stats.DiffStats.FilesChanged = output.FilesChanged pr.Stats.DiffStats.FilesChanged = output.FilesChanged
updateMergeStatus(pr)
return pr, nil return pr, nil
} }
func updateMergeStatus(pr *types.PullReq) {
mc := ""
if pr.MergeConflicts != nil {
mc = strings.TrimSpace(*pr.MergeConflicts)
}
switch {
case pr.State == enum.PullReqStateClosed:
pr.MergeStatus = enum.MergeStatusClosed
case pr.IsDraft:
pr.MergeStatus = enum.MergeStatusDraft
case mc != "":
pr.MergeStatus = enum.MergeStatusConflict
case pr.MergeRefSHA != nil:
pr.MergeStatus = enum.MergeStatusMergeable
default:
pr.MergeStatus = enum.MergeStatusUnchecked
}
}

View File

@ -64,9 +64,5 @@ func (c *Controller) List(
return nil, 0, err return nil, 0, err
} }
for i := range list {
updateMergeStatus(list[i])
}
return list, count, nil return list, count, nil
} }

View File

@ -122,9 +122,12 @@ func (c *Controller) State(ctx context.Context,
pr.IsDraft = in.IsDraft pr.IsDraft = in.IsDraft
pr.Edited = time.Now().UnixMilli() pr.Edited = time.Now().UnixMilli()
if in.State == enum.PullReqStateClosed { if in.State == enum.PullReqStateClosed {
pr.MergeRefSHA = nil // clear all merge (check) related fields
pr.MergeCheckStatus = enum.MergeCheckStatusUnchecked
pr.MergeTargetSHA = nil
pr.MergeBaseSHA = nil pr.MergeBaseSHA = nil
pr.MergeHeadSHA = nil pr.MergeSHA = nil
pr.MergeConflicts = nil
} }
pr.ActivitySeq++ // because we need to add the activity entry pr.ActivitySeq++ // because we need to add the activity entry
return nil return nil

View File

@ -50,7 +50,7 @@ func (c *Controller) MergeCheck(
HeadRepoUID: writeParams.RepoUID, // forks are not supported for now HeadRepoUID: writeParams.RepoUID, // forks are not supported for now
HeadBranch: info.HeadRef, HeadBranch: info.HeadRef,
}) })
if errors.Is(err, gitrpc.ErrPreconditionFailed) { if errors.Is(err, gitrpc.ErrNotMergeable) {
return MergeCheck{ return MergeCheck{
Mergeable: false, Mergeable: false,
}, nil }, nil

View File

@ -60,6 +60,8 @@ func Translate(err error) *Error {
return ErrNotFound return ErrNotFound
case errors.Is(err, gitrpc.ErrPreconditionFailed): case errors.Is(err, gitrpc.ErrPreconditionFailed):
return ErrPreconditionFailed return ErrPreconditionFailed
case errors.Is(err, gitrpc.ErrNotMergeable):
return ErrNotMergeable
// webhook errors // webhook errors
case errors.Is(err, webhook.ErrWebhookNotRetriggerable): case errors.Is(err, webhook.ErrWebhookNotRetriggerable):

View File

@ -31,6 +31,9 @@ var (
// ErrPreconditionFailed is returned when a precondition failed. // ErrPreconditionFailed is returned when a precondition failed.
ErrPreconditionFailed = New(http.StatusPreconditionFailed, "Precondition failed") ErrPreconditionFailed = New(http.StatusPreconditionFailed, "Precondition failed")
// ErrNotMergeable is returned when a branch can't be merged.
ErrNotMergeable = New(http.StatusPreconditionFailed, "Branch can't be merged")
// ErrNoChange is returned when no change was found based on the request. // ErrNoChange is returned when no change was found based on the request.
ErrNoChange = New(http.StatusBadRequest, "No Change") ErrNoChange = New(http.StatusBadRequest, "No Change")

View File

@ -97,9 +97,9 @@ const MergedEvent events.EventType = "merged"
type MergedPayload struct { type MergedPayload struct {
Base Base
MergeMethod enum.MergeMethod `json:"merge_method"` MergeMethod enum.MergeMethod `json:"merge_method"`
MergedSHA string `json:"merged_sha"` MergeSHA string `json:"merge_sha"`
BaseSHA string `json:"base_sha"` TargetSHA string `json:"target_sha"`
HeadSHA string `json:"head_sha"` SourceSHA string `json:"source_sha"`
} }
func (r *Reporter) Merged(ctx context.Context, payload *MergedPayload) { func (r *Reporter) Merged(ctx context.Context, payload *MergedPayload) {

View File

@ -31,10 +31,15 @@ func (s *Service) triggerPREventOnBranchUpdate(ctx context.Context,
"failed to set SourceSHA for PR %d to value '%s', expected SHA '%s' but current pr has '%s'", "failed to set SourceSHA for PR %d to value '%s', expected SHA '%s' but current pr has '%s'",
pr.Number, event.Payload.NewSHA, event.Payload.OldSHA, pr.SourceSHA) pr.Number, event.Payload.NewSHA, event.Payload.OldSHA, pr.SourceSHA)
} }
pr.SourceSHA = event.Payload.NewSHA pr.SourceSHA = event.Payload.NewSHA
pr.MergeRefSHA = nil
// reset merge-check fields for new run
pr.MergeCheckStatus = enum.MergeCheckStatusUnchecked
pr.MergeTargetSHA = nil
pr.MergeBaseSHA = nil pr.MergeBaseSHA = nil
pr.MergeHeadSHA = nil pr.MergeSHA = nil
pr.MergeConflicts = nil
return nil return nil
}) })
if err != nil { if err != nil {
@ -76,11 +81,15 @@ func (s *Service) closePullReqOnBranchDelete(ctx context.Context,
) error { ) error {
s.forEveryOpenPR(ctx, event.Payload.RepoID, event.Payload.Ref, func(pr *types.PullReq) error { s.forEveryOpenPR(ctx, event.Payload.RepoID, event.Payload.Ref, func(pr *types.PullReq) error {
pr, err := s.pullreqStore.UpdateOptLock(ctx, pr, func(pr *types.PullReq) error { pr, err := s.pullreqStore.UpdateOptLock(ctx, pr, func(pr *types.PullReq) error {
pr.State = enum.PullReqStateClosed
pr.MergeRefSHA = nil
pr.MergeBaseSHA = nil
pr.MergeHeadSHA = nil
pr.ActivitySeq++ // because we need to write the activity pr.ActivitySeq++ // because we need to write the activity
pr.State = enum.PullReqStateClosed
pr.MergeCheckStatus = enum.MergeCheckStatusUnchecked
pr.MergeTargetSHA = nil
pr.MergeBaseSHA = nil
pr.MergeSHA = nil
pr.MergeConflicts = nil
return nil return nil
}) })
if err != nil { if err != nil {

View File

@ -207,23 +207,36 @@ func (s *Service) updateMergeData(
Force: true, Force: true,
}) })
if errors.Is(err, gitrpc.ErrPreconditionFailed) { if errors.Is(err, gitrpc.ErrPreconditionFailed) {
// TODO: in case of merge conflict, update conflicts in pr entry - for that we need a return events.NewDiscardEventErrorf("Source branch '%s' is not on SHA '%s' anymore.",
// nice way to distinguish between the two errors.
return events.NewDiscardEventErrorf("Source branch '%s' is not on SHA '%s' anymore or is not mergeable.",
pr.SourceBranch, newSHA) pr.SourceBranch, newSHA)
} }
if err != nil {
isNotMergeableError := errors.Is(err, gitrpc.ErrNotMergeable)
if err != nil && !isNotMergeableError {
return fmt.Errorf("merge check failed for %s and %s with err: %w", return fmt.Errorf("merge check failed for %s and %s with err: %w",
targetRepo.UID+":"+pr.TargetBranch, sourceRepo.UID+":"+pr.SourceBranch, err) targetRepo.UID+":"+pr.TargetBranch, sourceRepo.UID+":"+pr.SourceBranch, err)
} }
// Update DB in both cases (failure or success)
_, err = s.pullreqStore.UpdateOptLock(ctx, pr, func(pr *types.PullReq) error { _, err = s.pullreqStore.UpdateOptLock(ctx, pr, func(pr *types.PullReq) error {
if pr.SourceSHA != newSHA { if pr.SourceSHA != newSHA {
return events.NewDiscardEventErrorf("PR SHA %s is newer than %s", pr.SourceSHA, newSHA) return events.NewDiscardEventErrorf("PR SHA %s is newer than %s", pr.SourceSHA, newSHA)
} }
pr.MergeRefSHA = &output.MergedSHA
pr.MergeBaseSHA = &output.BaseSHA if isNotMergeableError {
pr.MergeHeadSHA = &output.HeadSHA // TODO: gitrpc should return sha's either way, and also conflicting files!
pr.MergeCheckStatus = enum.MergeCheckStatusConflict
pr.MergeTargetSHA = nil
pr.MergeBaseSHA = nil
pr.MergeSHA = nil
pr.MergeConflicts = nil
} else {
pr.MergeCheckStatus = enum.MergeCheckStatusMergeable
pr.MergeTargetSHA = &output.BaseSHA
pr.MergeBaseSHA = &output.MergeBaseSHA
pr.MergeSHA = &output.MergeSHA
pr.MergeConflicts = nil
}
return nil return nil
}) })
if err != nil { if err != nil {

View File

@ -99,7 +99,7 @@ func pullReqInfoFrom(pr *types.PullReq) PullReqInfo {
SourceBranch: pr.SourceBranch, SourceBranch: pr.SourceBranch,
TargetRepoID: pr.TargetRepoID, TargetRepoID: pr.TargetRepoID,
TargetBranch: pr.TargetBranch, TargetBranch: pr.TargetBranch,
MergeStrategy: pr.MergeStrategy, MergeStrategy: pr.MergeMethod,
} }
} }

View File

@ -19,10 +19,11 @@ pullreq_id SERIAL PRIMARY KEY
,pullreq_activity_seq INTEGER DEFAULT 0 ,pullreq_activity_seq INTEGER DEFAULT 0
,pullreq_merged_by INTEGER ,pullreq_merged_by INTEGER
,pullreq_merged BIGINT ,pullreq_merged BIGINT
,pullreq_merge_strategy TEXT ,pullreq_merge_method TEXT
,pullreq_merge_head_sha TEXT ,pullreq_merge_check_status TEXT
,pullreq_merge_target_sha TEXT
,pullreq_merge_base_sha TEXT ,pullreq_merge_base_sha TEXT
,pullreq_merge_ref_sha TEXT ,pullreq_merge_sha TEXT
,pullreq_merge_conflicts TEXT ,pullreq_merge_conflicts TEXT
,CONSTRAINT fk_pullreq_created_by FOREIGN KEY (pullreq_created_by) ,CONSTRAINT fk_pullreq_created_by FOREIGN KEY (pullreq_created_by)
REFERENCES principals (principal_id) MATCH SIMPLE REFERENCES principals (principal_id) MATCH SIMPLE

View File

@ -19,10 +19,11 @@ pullreq_id INTEGER PRIMARY KEY AUTOINCREMENT
,pullreq_activity_seq INTEGER DEFAULT 0 ,pullreq_activity_seq INTEGER DEFAULT 0
,pullreq_merged_by INTEGER ,pullreq_merged_by INTEGER
,pullreq_merged BIGINT ,pullreq_merged BIGINT
,pullreq_merge_strategy TEXT ,pullreq_merge_method TEXT
,pullreq_merge_head_sha TEXT ,pullreq_merge_check_status TEXT NOT NULL
,pullreq_merge_target_sha TEXT
,pullreq_merge_base_sha TEXT ,pullreq_merge_base_sha TEXT
,pullreq_merge_ref_sha TEXT ,pullreq_merge_sha TEXT
,pullreq_merge_conflicts TEXT ,pullreq_merge_conflicts TEXT
,CONSTRAINT fk_pullreq_created_by FOREIGN KEY (pullreq_created_by) ,CONSTRAINT fk_pullreq_created_by FOREIGN KEY (pullreq_created_by)
REFERENCES principals (principal_id) MATCH SIMPLE REFERENCES principals (principal_id) MATCH SIMPLE

View File

@ -67,13 +67,15 @@ type pullReq struct {
ActivitySeq int64 `db:"pullreq_activity_seq"` ActivitySeq int64 `db:"pullreq_activity_seq"`
MergedBy null.Int `db:"pullreq_merged_by"` MergedBy null.Int `db:"pullreq_merged_by"`
Merged null.Int `db:"pullreq_merged"` Merged null.Int `db:"pullreq_merged"`
MergeStrategy null.String `db:"pullreq_merge_strategy"` MergeMethod null.String `db:"pullreq_merge_method"`
MergeHeadSHA null.String `db:"pullreq_merge_head_sha"`
MergeBaseSHA null.String `db:"pullreq_merge_base_sha"` MergeCheckStatus enum.MergeCheckStatus `db:"pullreq_merge_check_status"`
MergeRefSHA null.String `db:"pullreq_merge_ref_sha"` MergeTargetSHA null.String `db:"pullreq_merge_target_sha"`
MergeConflicts null.String `db:"pullreq_merge_conflicts"` MergeBaseSHA null.String `db:"pullreq_merge_base_sha"`
MergeSHA null.String `db:"pullreq_merge_sha"`
MergeConflicts null.String `db:"pullreq_merge_conflicts"`
} }
const ( const (
@ -98,10 +100,11 @@ const (
,pullreq_activity_seq ,pullreq_activity_seq
,pullreq_merged_by ,pullreq_merged_by
,pullreq_merged ,pullreq_merged
,pullreq_merge_strategy ,pullreq_merge_method
,pullreq_merge_head_sha ,pullreq_merge_check_status
,pullreq_merge_target_sha
,pullreq_merge_base_sha ,pullreq_merge_base_sha
,pullreq_merge_ref_sha ,pullreq_merge_sha
,pullreq_merge_conflicts` ,pullreq_merge_conflicts`
pullReqSelectBase = ` pullReqSelectBase = `
@ -185,8 +188,11 @@ func (s *PullReqStore) Create(ctx context.Context, pr *types.PullReq) error {
,pullreq_activity_seq ,pullreq_activity_seq
,pullreq_merged_by ,pullreq_merged_by
,pullreq_merged ,pullreq_merged
,pullreq_merge_strategy ,pullreq_merge_method
,pullreq_merge_ref_sha ,pullreq_merge_check_status
,pullreq_merge_target_sha
,pullreq_merge_base_sha
,pullreq_merge_sha
,pullreq_merge_conflicts ,pullreq_merge_conflicts
) values ( ) values (
:pullreq_version :pullreq_version
@ -208,8 +214,11 @@ func (s *PullReqStore) Create(ctx context.Context, pr *types.PullReq) error {
,:pullreq_activity_seq ,:pullreq_activity_seq
,:pullreq_merged_by ,:pullreq_merged_by
,:pullreq_merged ,:pullreq_merged
,:pullreq_merge_strategy ,:pullreq_merge_method
,:pullreq_merge_ref_sha ,:pullreq_merge_check_status
,:pullreq_merge_target_sha
,:pullreq_merge_base_sha
,:pullreq_merge_sha
,:pullreq_merge_conflicts ,:pullreq_merge_conflicts
) RETURNING pullreq_id` ) RETURNING pullreq_id`
@ -244,10 +253,11 @@ func (s *PullReqStore) Update(ctx context.Context, pr *types.PullReq) error {
,pullreq_source_sha = :pullreq_source_sha ,pullreq_source_sha = :pullreq_source_sha
,pullreq_merged_by = :pullreq_merged_by ,pullreq_merged_by = :pullreq_merged_by
,pullreq_merged = :pullreq_merged ,pullreq_merged = :pullreq_merged
,pullreq_merge_strategy = :pullreq_merge_strategy ,pullreq_merge_method = :pullreq_merge_method
,pullreq_merge_head_sha = :pullreq_merge_head_sha ,pullreq_merge_check_status = :pullreq_merge_check_status
,pullreq_merge_target_sha = :pullreq_merge_target_sha
,pullreq_merge_base_sha = :pullreq_merge_base_sha ,pullreq_merge_base_sha = :pullreq_merge_base_sha
,pullreq_merge_ref_sha = :pullreq_merge_ref_sha ,pullreq_merge_sha = :pullreq_merge_sha
,pullreq_merge_conflicts = :pullreq_merge_conflicts ,pullreq_merge_conflicts = :pullreq_merge_conflicts
WHERE pullreq_id = :pullreq_id AND pullreq_version = :pullreq_version - 1` WHERE pullreq_id = :pullreq_id AND pullreq_version = :pullreq_version - 1`
@ -448,33 +458,34 @@ func (s *PullReqStore) List(ctx context.Context, opts *types.PullReqFilter) ([]*
func mapPullReq(pr *pullReq) *types.PullReq { func mapPullReq(pr *pullReq) *types.PullReq {
return &types.PullReq{ return &types.PullReq{
ID: pr.ID, ID: pr.ID,
Version: pr.Version, Version: pr.Version,
Number: pr.Number, Number: pr.Number,
CreatedBy: pr.CreatedBy, CreatedBy: pr.CreatedBy,
Created: pr.Created, Created: pr.Created,
Updated: pr.Updated, Updated: pr.Updated,
Edited: pr.Edited, Edited: pr.Edited,
State: pr.State, State: pr.State,
IsDraft: pr.IsDraft, IsDraft: pr.IsDraft,
CommentCount: pr.CommentCount, CommentCount: pr.CommentCount,
Title: pr.Title, Title: pr.Title,
Description: pr.Description, Description: pr.Description,
SourceRepoID: pr.SourceRepoID, SourceRepoID: pr.SourceRepoID,
SourceBranch: pr.SourceBranch, SourceBranch: pr.SourceBranch,
SourceSHA: pr.SourceSHA, SourceSHA: pr.SourceSHA,
TargetRepoID: pr.TargetRepoID, TargetRepoID: pr.TargetRepoID,
TargetBranch: pr.TargetBranch, TargetBranch: pr.TargetBranch,
ActivitySeq: pr.ActivitySeq, ActivitySeq: pr.ActivitySeq,
MergedBy: pr.MergedBy.Ptr(), MergedBy: pr.MergedBy.Ptr(),
Merged: pr.Merged.Ptr(), Merged: pr.Merged.Ptr(),
MergeStrategy: (*enum.MergeMethod)(pr.MergeStrategy.Ptr()), MergeMethod: (*enum.MergeMethod)(pr.MergeMethod.Ptr()),
MergeHeadSHA: pr.MergeHeadSHA.Ptr(), MergeCheckStatus: pr.MergeCheckStatus,
MergeBaseSHA: pr.MergeBaseSHA.Ptr(), MergeTargetSHA: pr.MergeTargetSHA.Ptr(),
MergeRefSHA: pr.MergeRefSHA.Ptr(), MergeBaseSHA: pr.MergeBaseSHA.Ptr(),
MergeConflicts: pr.MergeConflicts.Ptr(), MergeSHA: pr.MergeSHA.Ptr(),
Author: types.PrincipalInfo{}, MergeConflicts: pr.MergeConflicts.Ptr(),
Merger: nil, Author: types.PrincipalInfo{},
Merger: nil,
Stats: types.PullReqStats{ Stats: types.PullReqStats{
Conversations: pr.CommentCount, Conversations: pr.CommentCount,
DiffStats: types.DiffStats{ DiffStats: types.DiffStats{
@ -487,31 +498,32 @@ func mapPullReq(pr *pullReq) *types.PullReq {
func mapInternalPullReq(pr *types.PullReq) *pullReq { func mapInternalPullReq(pr *types.PullReq) *pullReq {
m := &pullReq{ m := &pullReq{
ID: pr.ID, ID: pr.ID,
Version: pr.Version, Version: pr.Version,
Number: pr.Number, Number: pr.Number,
CreatedBy: pr.CreatedBy, CreatedBy: pr.CreatedBy,
Created: pr.Created, Created: pr.Created,
Updated: pr.Updated, Updated: pr.Updated,
Edited: pr.Edited, Edited: pr.Edited,
State: pr.State, State: pr.State,
IsDraft: pr.IsDraft, IsDraft: pr.IsDraft,
CommentCount: pr.CommentCount, CommentCount: pr.CommentCount,
Title: pr.Title, Title: pr.Title,
Description: pr.Description, Description: pr.Description,
SourceRepoID: pr.SourceRepoID, SourceRepoID: pr.SourceRepoID,
SourceBranch: pr.SourceBranch, SourceBranch: pr.SourceBranch,
SourceSHA: pr.SourceSHA, SourceSHA: pr.SourceSHA,
TargetRepoID: pr.TargetRepoID, TargetRepoID: pr.TargetRepoID,
TargetBranch: pr.TargetBranch, TargetBranch: pr.TargetBranch,
ActivitySeq: pr.ActivitySeq, ActivitySeq: pr.ActivitySeq,
MergedBy: null.IntFromPtr(pr.MergedBy), MergedBy: null.IntFromPtr(pr.MergedBy),
Merged: null.IntFromPtr(pr.Merged), Merged: null.IntFromPtr(pr.Merged),
MergeStrategy: null.StringFromPtr((*string)(pr.MergeStrategy)), MergeMethod: null.StringFromPtr((*string)(pr.MergeMethod)),
MergeHeadSHA: null.StringFromPtr(pr.MergeHeadSHA), MergeCheckStatus: pr.MergeCheckStatus,
MergeBaseSHA: null.StringFromPtr(pr.MergeBaseSHA), MergeTargetSHA: null.StringFromPtr(pr.MergeTargetSHA),
MergeRefSHA: null.StringFromPtr(pr.MergeRefSHA), MergeBaseSHA: null.StringFromPtr(pr.MergeBaseSHA),
MergeConflicts: null.StringFromPtr(pr.MergeConflicts), MergeSHA: null.StringFromPtr(pr.MergeSHA),
MergeConflicts: null.StringFromPtr(pr.MergeConflicts),
} }
return m return m

View File

@ -189,12 +189,13 @@ var mergeMethods = sortEnum([]MergeMethod{
MergeMethodRebase, MergeMethodRebase,
}) })
type MergeStatus string type MergeCheckStatus string
const ( const (
MergeStatusUnchecked MergeStatus = "unchecked" // The merge status has not been checked. // MergeCheckStatusUnchecked merge status has not been checked.
MergeStatusConflict MergeStatus = "conflict" // Cant merge into the target branch due to a potential conflict. MergeCheckStatusUnchecked MergeCheckStatus = "unchecked"
MergeStatusDraft MergeStatus = "draft_pr" // Cant merge because the pull request is a draft. // MergeCheckStatusConflict cant merge into the target branch due to a potential conflict.
MergeStatusClosed MergeStatus = "closed" // The merge request must be open before merge. MergeCheckStatusConflict MergeCheckStatus = "conflict"
MergeStatusMergeable MergeStatus = "mergeable" // The branch can merge cleanly into the target branch. // MergeCheckStatusMergeable branch can merged cleanly into the target branch.
MergeCheckStatusMergeable MergeCheckStatus = "mergeable"
) )

View File

@ -35,15 +35,15 @@ type PullReq struct {
ActivitySeq int64 `json:"-"` // not returned, because it's a server's internal field ActivitySeq int64 `json:"-"` // not returned, because it's a server's internal field
MergedBy *int64 `json:"-"` // not returned, because the merger info is in the Merger field MergedBy *int64 `json:"-"` // not returned, because the merger info is in the Merger field
Merged *int64 `json:"merged"` Merged *int64 `json:"merged"`
MergeStrategy *enum.MergeMethod `json:"merge_strategy"` MergeMethod *enum.MergeMethod `json:"merge_method"`
MergeHeadSHA *string `json:"merge_head_sha"` MergeCheckStatus enum.MergeCheckStatus `json:"merge_check_status"`
MergeBaseSHA *string `json:"merge_base_sha"` MergeTargetSHA *string `json:"merge_target_sha"`
MergeRefSHA *string `json:"merge_ref_sha"` MergeBaseSHA *string `json:"merge_base_sha"`
MergeStatus enum.MergeStatus `json:"merge_status"` MergeSHA *string `json:"merge_sha"`
MergeConflicts *string `json:"merge_conflicts,omitempty"` MergeConflicts *string `json:"merge_conflicts,omitempty"`
Author PrincipalInfo `json:"author"` Author PrincipalInfo `json:"author"`
Merger *PrincipalInfo `json:"merger"` Merger *PrincipalInfo `json:"merger"`

View File

@ -166,9 +166,9 @@ func (a *PullRequestActivityPayloadComment) ActivityType() enum.PullReqActivityT
type PullRequestActivityPayloadMerge struct { type PullRequestActivityPayloadMerge struct {
MergeMethod enum.MergeMethod `json:"merge_method"` MergeMethod enum.MergeMethod `json:"merge_method"`
MergedSHA string `json:"merged_sha"` MergeSHA string `json:"merge_sha"`
BaseSHA string `json:"base_sha"` TargetSHA string `json:"target_sha"`
HeadSHA string `json:"head_sha"` SourceSHA string `json:"source_sha"`
} }
func (a *PullRequestActivityPayloadMerge) ActivityType() enum.PullReqActivityType { func (a *PullRequestActivityPayloadMerge) ActivityType() enum.PullReqActivityType {