mirror of
https://github.com/harness/drone.git
synced 2025-05-05 15:32:56 +00:00
feat: [CODE-3076]: update several refs on merge (#3355)
* resolve pr comments * update several refs on merge
This commit is contained in:
parent
898ecb123c
commit
530a707052
@ -177,13 +177,7 @@ func (c *Controller) Merge(
|
||||
}
|
||||
|
||||
sourceRepo := targetRepo
|
||||
sourceWriteParams := targetWriteParams
|
||||
if pr.SourceRepoID != pr.TargetRepoID {
|
||||
sourceWriteParams, err = controller.CreateRPCInternalWriteParams(ctx, c.urlProvider, session, sourceRepo)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create RPC write params: %w", err)
|
||||
}
|
||||
|
||||
sourceRepo, err = c.repoStore.Find(ctx, pr.SourceRepoID)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to get source repository: %w", err)
|
||||
@ -195,7 +189,7 @@ func (c *Controller) Merge(
|
||||
return nil, nil, fmt.Errorf("failed to fetch rules: %w", err)
|
||||
}
|
||||
|
||||
checkResults, err := c.checkStore.ListResults(ctx, targetRepo.ID, pr.SourceSHA)
|
||||
checkResults, err := c.checkStore.ListResults(ctx, targetRepo.ID, in.SourceSHA)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to list status checks: %w", err)
|
||||
}
|
||||
@ -292,7 +286,7 @@ func (c *Controller) Merge(
|
||||
BaseBranch: pr.TargetBranch,
|
||||
HeadRepoUID: sourceRepo.GitUID,
|
||||
HeadBranch: pr.SourceBranch,
|
||||
RefType: gitenum.RefTypeUndefined, // update no refs -> no commit will be created
|
||||
Refs: nil, // update no refs -> no commit will be created
|
||||
HeadExpectedSHA: sha.Must(in.SourceSHA),
|
||||
Method: gitenum.MergeMethod(in.Method),
|
||||
})
|
||||
@ -425,6 +419,65 @@ func (c *Controller) Merge(
|
||||
|
||||
log.Ctx(ctx).Debug().Msgf("all pre-check passed, merge PR")
|
||||
|
||||
sourceBranchSHA, err := sha.New(in.SourceSHA)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to convert source SHA: %w", err)
|
||||
}
|
||||
|
||||
refSourceBranch, err := git.GetRefPath(pr.SourceBranch, gitenum.RefTypeBranch)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to generate source branch ref name: %w", err)
|
||||
}
|
||||
|
||||
refTargetBranch, err := git.GetRefPath(pr.TargetBranch, gitenum.RefTypeBranch)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to generate target branch ref name: %w", err)
|
||||
}
|
||||
|
||||
prNumber := strconv.FormatInt(pr.Number, 10)
|
||||
|
||||
refPullReqHead, err := git.GetRefPath(prNumber, gitenum.RefTypePullReqHead)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to generate pull request head ref name: %w", err)
|
||||
}
|
||||
|
||||
refPullReqMerge, err := git.GetRefPath(prNumber, gitenum.RefTypePullReqMerge)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to generate pull requert merge ref name: %w", err)
|
||||
}
|
||||
|
||||
refUpdates := make([]git.RefUpdate, 0, 4)
|
||||
|
||||
// Update the target branch to the result of the merge.
|
||||
refUpdates = append(refUpdates, git.RefUpdate{
|
||||
Name: refTargetBranch,
|
||||
Old: sha.SHA{}, // don't care about the current commit SHA of the target branch.
|
||||
New: sha.SHA{}, // update to the result of the merge.
|
||||
})
|
||||
|
||||
// Make sure the PR head ref points to the correct commit after the merge.
|
||||
refUpdates = append(refUpdates, git.RefUpdate{
|
||||
Name: refPullReqHead,
|
||||
Old: sha.SHA{}, // don't care about the old value.
|
||||
New: sourceBranchSHA,
|
||||
})
|
||||
|
||||
// Delete the PR merge reference.
|
||||
refUpdates = append(refUpdates, git.RefUpdate{
|
||||
Name: refPullReqMerge,
|
||||
Old: sha.SHA{},
|
||||
New: sha.Nil,
|
||||
})
|
||||
|
||||
if ruleOut.DeleteSourceBranch {
|
||||
// Delete the source branch.
|
||||
refUpdates = append(refUpdates, git.RefUpdate{
|
||||
Name: refSourceBranch,
|
||||
Old: sourceBranchSHA,
|
||||
New: sha.Nil,
|
||||
})
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
mergeOutput, err := c.git.Merge(ctx, &git.MergeParams{
|
||||
WriteParams: targetWriteParams,
|
||||
@ -436,8 +489,7 @@ func (c *Controller) Merge(
|
||||
CommitterDate: &now,
|
||||
Author: author,
|
||||
AuthorDate: &now,
|
||||
RefType: gitenum.RefTypeBranch,
|
||||
RefName: pr.TargetBranch,
|
||||
Refs: refUpdates,
|
||||
HeadExpectedSHA: sha.Must(in.SourceSHA),
|
||||
Method: gitenum.MergeMethod(in.Method),
|
||||
})
|
||||
@ -545,20 +597,7 @@ func (c *Controller) Merge(
|
||||
SourceSHA: mergeOutput.HeadSHA.String(),
|
||||
})
|
||||
|
||||
var branchDeleted bool
|
||||
if ruleOut.DeleteSourceBranch {
|
||||
errDelete := c.git.DeleteBranch(ctx, &git.DeleteBranchParams{
|
||||
WriteParams: sourceWriteParams,
|
||||
BranchName: pr.SourceBranch,
|
||||
})
|
||||
if errDelete != nil {
|
||||
// non-critical error
|
||||
log.Ctx(ctx).Err(errDelete).Msgf("failed to delete source branch after merging")
|
||||
} else {
|
||||
branchDeleted = true
|
||||
|
||||
// NOTE: there is a chance someone pushed on the branch between merge and delete.
|
||||
// Either way, we'll use the SHA that was merged with for the activity to be consistent from PR perspective.
|
||||
pr.ActivitySeq = activitySeqBranchDeleted
|
||||
if _, errAct := c.activityStore.CreateWithPayload(ctx, pr, mergedBy,
|
||||
&types.PullRequestActivityPayloadBranchDelete{SHA: in.SourceSHA}, nil); errAct != nil {
|
||||
@ -567,7 +606,6 @@ func (c *Controller) Merge(
|
||||
Msgf("failed to write pull request activity for successful automatic branch delete")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.sseStreamer.Publish(ctx, targetRepo.ParentID, enum.SSETypePullReqUpdated, pr)
|
||||
|
||||
@ -621,7 +659,7 @@ func (c *Controller) Merge(
|
||||
}
|
||||
return &types.MergeResponse{
|
||||
SHA: mergeOutput.MergeSHA.String(),
|
||||
BranchDeleted: branchDeleted,
|
||||
BranchDeleted: ruleOut.DeleteSourceBranch,
|
||||
RuleViolations: violations,
|
||||
}, nil, nil
|
||||
}
|
||||
|
@ -189,7 +189,7 @@ func (c *Controller) Create(
|
||||
Name: strconv.FormatInt(targetRepo.PullReqSeq, 10),
|
||||
Type: gitenum.RefTypePullReqHead,
|
||||
NewValue: sourceSHA,
|
||||
OldValue: sha.None, // this is a new pull request, so we expect that the ref doesn't exist
|
||||
OldValue: sha.None, // we don't care about the old value
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create PR head ref: %w", err)
|
||||
|
@ -168,11 +168,21 @@ func (c *Controller) State(ctx context.Context,
|
||||
pr.Closed = &nowMilli
|
||||
pr.MarkAsMergeUnchecked()
|
||||
|
||||
// delete the merge pull request reference
|
||||
err = c.git.UpdateRef(ctx, git.UpdateRefParams{
|
||||
WriteParams: targetWriteParams,
|
||||
Name: strconv.FormatInt(pr.Number, 10),
|
||||
Type: gitenum.RefTypePullReqMerge,
|
||||
NewValue: sha.Nil,
|
||||
OldValue: sha.None, // we don't care about the old value
|
||||
})
|
||||
|
||||
case changeReopen:
|
||||
pr.SourceSHA = sourceSHA.String()
|
||||
pr.MergeBaseSHA = mergeBaseSHA.String()
|
||||
pr.Closed = nil
|
||||
|
||||
// create the head pull request reference
|
||||
err = c.git.UpdateRef(ctx, git.UpdateRefParams{
|
||||
WriteParams: targetWriteParams,
|
||||
Name: strconv.FormatInt(pr.Number, 10),
|
||||
|
@ -157,11 +157,18 @@ func (c *Controller) Rebase(
|
||||
return nil, nil, fmt.Errorf("failed to create RPC write params: %w", err)
|
||||
}
|
||||
|
||||
refType := gitenum.RefTypeBranch
|
||||
refName := in.HeadBranch
|
||||
if in.DryRun {
|
||||
refType = gitenum.RefTypeUndefined
|
||||
refName = ""
|
||||
var refs []git.RefUpdate
|
||||
if !in.DryRun {
|
||||
headBranchRef, err := git.GetRefPath(in.HeadBranch, gitenum.RefTypeBranch)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to gerenere ref name: %w", err)
|
||||
}
|
||||
|
||||
refs = append(refs, git.RefUpdate{
|
||||
Name: headBranchRef,
|
||||
Old: headBranch.Branch.SHA,
|
||||
New: sha.SHA{}, // update to the result of the merge
|
||||
})
|
||||
}
|
||||
|
||||
mergeOutput, err := c.git.Merge(ctx, &git.MergeParams{
|
||||
@ -169,8 +176,7 @@ func (c *Controller) Rebase(
|
||||
BaseSHA: baseCommitSHA,
|
||||
HeadRepoUID: repo.GitUID,
|
||||
HeadBranch: in.HeadBranch,
|
||||
RefType: refType,
|
||||
RefName: refName,
|
||||
Refs: refs,
|
||||
HeadExpectedSHA: in.HeadCommitSHA,
|
||||
Method: gitenum.MergeMethodRebase,
|
||||
})
|
||||
|
@ -135,6 +135,11 @@ func (c *Controller) Squash(
|
||||
in.HeadCommitSHA, headBranch.Branch.Name)
|
||||
}
|
||||
|
||||
headBranchRef, err := git.GetRefPath(in.HeadBranch, gitenum.RefTypeBranch)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to gerenere ref name: %w", err)
|
||||
}
|
||||
|
||||
baseCommitSHA := in.BaseCommitSHA
|
||||
if baseCommitSHA.IsEmpty() {
|
||||
baseBranch, err := c.git.GetBranch(ctx, &git.GetBranchParams{
|
||||
@ -153,11 +158,13 @@ func (c *Controller) Squash(
|
||||
return nil, nil, fmt.Errorf("failed to create RPC write params: %w", err)
|
||||
}
|
||||
|
||||
refType := gitenum.RefTypeBranch
|
||||
refName := in.HeadBranch
|
||||
if in.DryRun {
|
||||
refType = gitenum.RefTypeUndefined
|
||||
refName = ""
|
||||
var refs []git.RefUpdate
|
||||
if !in.DryRun {
|
||||
refs = append(refs, git.RefUpdate{
|
||||
Name: headBranchRef,
|
||||
Old: headBranch.Branch.SHA,
|
||||
New: sha.SHA{}, // update to the result of the merge
|
||||
})
|
||||
}
|
||||
|
||||
mergeBase, err := c.git.MergeBase(ctx, git.MergeBaseParams{
|
||||
@ -223,8 +230,7 @@ func (c *Controller) Squash(
|
||||
HeadRepoUID: repo.GitUID,
|
||||
HeadBranch: in.HeadBranch,
|
||||
Message: git.CommitMessage(in.Title, in.Message),
|
||||
RefType: refType,
|
||||
RefName: refName,
|
||||
Refs: refs,
|
||||
Committer: committer,
|
||||
CommitterDate: &now,
|
||||
Author: author,
|
||||
|
@ -79,46 +79,6 @@ func (s *Service) mergeCheckOnReopen(ctx context.Context,
|
||||
)
|
||||
}
|
||||
|
||||
// mergeCheckOnClosed deletes the merge ref.
|
||||
func (s *Service) mergeCheckOnClosed(ctx context.Context,
|
||||
event *events.Event[*pullreqevents.ClosedPayload],
|
||||
) error {
|
||||
return s.deleteMergeRef(ctx, event.Payload.SourceRepoID, event.Payload.Number)
|
||||
}
|
||||
|
||||
// mergeCheckOnMerged deletes the merge ref.
|
||||
func (s *Service) mergeCheckOnMerged(ctx context.Context,
|
||||
event *events.Event[*pullreqevents.MergedPayload],
|
||||
) error {
|
||||
return s.deleteMergeRef(ctx, event.Payload.SourceRepoID, event.Payload.Number)
|
||||
}
|
||||
|
||||
func (s *Service) deleteMergeRef(ctx context.Context, repoID int64, prNum int64) error {
|
||||
repo, err := s.repoGitInfoCache.Get(ctx, repoID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get repo with ID %d: %w", repoID, err)
|
||||
}
|
||||
|
||||
writeParams, err := createSystemRPCWriteParams(ctx, s.urlProvider, repo.ID, repo.GitUID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate rpc write params: %w", err)
|
||||
}
|
||||
|
||||
// TODO: This doesn't work for forked repos
|
||||
err = s.git.UpdateRef(ctx, git.UpdateRefParams{
|
||||
WriteParams: writeParams,
|
||||
Name: strconv.Itoa(int(prNum)),
|
||||
Type: gitenum.RefTypePullReqMerge,
|
||||
NewValue: sha.None, // when NewValue is empty will delete the ref.
|
||||
OldValue: sha.None, // we don't care about the old value
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to remove PR merge ref: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint:funlen // refactor if required.
|
||||
func (s *Service) updateMergeData(
|
||||
ctx context.Context,
|
||||
@ -186,6 +146,19 @@ func (s *Service) updateMergeData(
|
||||
return fmt.Errorf("failed to generate rpc write params: %w", err)
|
||||
}
|
||||
|
||||
refName, err := git.GetRefPath(strconv.Itoa(int(pr.Number)), gitenum.RefTypePullReqMerge)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate pull request merge ref name: %w", err)
|
||||
}
|
||||
|
||||
refs := []git.RefUpdate{
|
||||
{
|
||||
Name: refName,
|
||||
Old: sha.SHA{}, // no matter what the value of the reference is
|
||||
New: sha.SHA{}, // update it to point to result of the merge
|
||||
},
|
||||
}
|
||||
|
||||
// call merge and store output in pr merge reference.
|
||||
now := time.Now()
|
||||
mergeOutput, err := s.git.Merge(ctx, &git.MergeParams{
|
||||
@ -193,8 +166,7 @@ func (s *Service) updateMergeData(
|
||||
BaseBranch: pr.TargetBranch,
|
||||
HeadRepoUID: sourceRepo.GitUID,
|
||||
HeadBranch: pr.SourceBranch,
|
||||
RefType: gitenum.RefTypePullReqMerge,
|
||||
RefName: strconv.Itoa(int(pr.Number)),
|
||||
Refs: refs,
|
||||
HeadExpectedSHA: sha.Must(newSHA),
|
||||
Force: true,
|
||||
|
||||
|
@ -194,8 +194,6 @@ func New(ctx context.Context,
|
||||
_ = r.RegisterCreated(service.mergeCheckOnCreated)
|
||||
_ = r.RegisterBranchUpdated(service.mergeCheckOnBranchUpdate)
|
||||
_ = r.RegisterReopened(service.mergeCheckOnReopen)
|
||||
_ = r.RegisterClosed(service.mergeCheckOnClosed)
|
||||
_ = r.RegisterMerged(service.mergeCheckOnMerged)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
@ -104,14 +104,14 @@ func (s *Service) CreateBranch(ctx context.Context, params *CreateBranchParams)
|
||||
return nil, fmt.Errorf("failed to get target commit: %w", err)
|
||||
}
|
||||
|
||||
branchRef := api.GetReferenceFromBranchName(params.BranchName)
|
||||
|
||||
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath, branchRef)
|
||||
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create ref updater to create the branch: %w", err)
|
||||
}
|
||||
|
||||
err = refUpdater.Do(ctx, sha.Nil, targetCommit.SHA)
|
||||
branchRef := api.GetReferenceFromBranchName(params.BranchName)
|
||||
|
||||
err = refUpdater.DoOne(ctx, branchRef, sha.Nil, targetCommit.SHA)
|
||||
if errors.IsConflict(err) {
|
||||
return nil, errors.Conflict("branch %q already exists", params.BranchName)
|
||||
}
|
||||
@ -162,15 +162,16 @@ func (s *Service) DeleteBranch(ctx context.Context, params *DeleteBranchParams)
|
||||
}
|
||||
|
||||
repoPath := getFullPathForRepo(s.reposRoot, params.RepoUID)
|
||||
branchRef := api.GetReferenceFromBranchName(params.BranchName)
|
||||
commitSha, _ := sha.NewOrEmpty(params.SHA)
|
||||
|
||||
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath, branchRef)
|
||||
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create ref updater to create the branch: %w", err)
|
||||
}
|
||||
|
||||
err = refUpdater.Do(ctx, commitSha, sha.Nil)
|
||||
branchRef := api.GetReferenceFromBranchName(params.BranchName)
|
||||
|
||||
err = refUpdater.DoOne(ctx, branchRef, commitSha, sha.Nil)
|
||||
if errors.IsNotFound(err) {
|
||||
return errors.NotFound("branch %q does not exist", params.BranchName)
|
||||
}
|
||||
|
@ -17,8 +17,7 @@ package enum
|
||||
type RefType int
|
||||
|
||||
const (
|
||||
RefTypeUndefined RefType = iota
|
||||
RefTypeRaw
|
||||
RefTypeRaw RefType = iota
|
||||
RefTypeBranch
|
||||
RefTypeTag
|
||||
RefTypePullReqHead
|
||||
@ -37,8 +36,6 @@ func (t RefType) String() string {
|
||||
return "head"
|
||||
case RefTypePullReqMerge:
|
||||
return "merge"
|
||||
case RefTypeUndefined:
|
||||
fallthrough
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/harness/gitness/errors"
|
||||
@ -32,7 +33,6 @@ func CreateRefUpdater(
|
||||
hookClientFactory ClientFactory,
|
||||
envVars map[string]string,
|
||||
repoPath string,
|
||||
ref string,
|
||||
) (*RefUpdater, error) {
|
||||
if repoPath == "" {
|
||||
return nil, errors.Internal(nil, "repo path can't be empty")
|
||||
@ -44,13 +44,11 @@ func CreateRefUpdater(
|
||||
}
|
||||
|
||||
return &RefUpdater{
|
||||
state: stateInitOld,
|
||||
state: stateInit,
|
||||
hookClient: client,
|
||||
envVars: envVars,
|
||||
repoPath: repoPath,
|
||||
ref: ref,
|
||||
oldValue: sha.None,
|
||||
newValue: sha.None,
|
||||
refs: nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -62,9 +60,7 @@ type RefUpdater struct {
|
||||
hookClient Client
|
||||
envVars map[string]string
|
||||
repoPath string
|
||||
ref string
|
||||
oldValue sha.SHA
|
||||
newValue sha.SHA
|
||||
refs []ReferenceUpdate
|
||||
}
|
||||
|
||||
// refUpdaterState represents state of the ref updater internal state machine.
|
||||
@ -72,10 +68,8 @@ type refUpdaterState byte
|
||||
|
||||
func (t refUpdaterState) String() string {
|
||||
switch t {
|
||||
case stateInitOld:
|
||||
return "INIT_OLD"
|
||||
case stateInitNew:
|
||||
return "INIT_NEW"
|
||||
case stateInit:
|
||||
return "INIT"
|
||||
case statePre:
|
||||
return "PRE"
|
||||
case stateUpdate:
|
||||
@ -89,8 +83,7 @@ func (t refUpdaterState) String() string {
|
||||
}
|
||||
|
||||
const (
|
||||
stateInitOld refUpdaterState = iota
|
||||
stateInitNew
|
||||
stateInit refUpdaterState = iota
|
||||
statePre
|
||||
stateUpdate
|
||||
statePost
|
||||
@ -98,8 +91,8 @@ const (
|
||||
)
|
||||
|
||||
// Do runs full ref update by executing all methods in the correct order.
|
||||
func (u *RefUpdater) Do(ctx context.Context, oldValue, newValue sha.SHA) error {
|
||||
if err := u.Init(ctx, oldValue, newValue); err != nil {
|
||||
func (u *RefUpdater) Do(ctx context.Context, refs []ReferenceUpdate) error {
|
||||
if err := u.Init(ctx, refs); err != nil {
|
||||
return fmt.Errorf("init failed: %w", err)
|
||||
}
|
||||
|
||||
@ -118,53 +111,42 @@ func (u *RefUpdater) Do(ctx context.Context, oldValue, newValue sha.SHA) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *RefUpdater) Init(ctx context.Context, oldValue, newValue sha.SHA) error {
|
||||
if err := u.InitOld(ctx, oldValue); err != nil {
|
||||
return fmt.Errorf("init old failed: %w", err)
|
||||
}
|
||||
if err := u.InitNew(ctx, newValue); err != nil {
|
||||
return fmt.Errorf("init new failed: %w", err)
|
||||
// DoOne runs full ref update of only one reference.
|
||||
func (u *RefUpdater) DoOne(ctx context.Context, ref string, oldValue, newValue sha.SHA) error {
|
||||
return u.Do(ctx, []ReferenceUpdate{
|
||||
{
|
||||
Ref: ref,
|
||||
Old: oldValue,
|
||||
New: newValue,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *RefUpdater) InitOld(ctx context.Context, oldValue sha.SHA) error {
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if u.state != stateInitOld {
|
||||
func (u *RefUpdater) Init(ctx context.Context, refs []ReferenceUpdate) error {
|
||||
if u.state != stateInit {
|
||||
return fmt.Errorf("invalid operation order: init old requires state=%s, current state=%s",
|
||||
stateInitOld, u.state)
|
||||
stateInit, u.state)
|
||||
}
|
||||
|
||||
u.refs = make([]ReferenceUpdate, 0, len(refs))
|
||||
for _, ref := range refs {
|
||||
oldValue := ref.Old
|
||||
newValue := ref.New
|
||||
|
||||
var oldValueKnown bool
|
||||
|
||||
if oldValue.IsEmpty() {
|
||||
// if no old value was provided, use current value (as required for hooks)
|
||||
val, err := u.getRef(ctx)
|
||||
val, err := u.getRef(ctx, ref.Ref)
|
||||
if errors.IsNotFound(err) { //nolint:gocritic
|
||||
oldValue = sha.Nil
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("failed to get current value of reference: %w", err)
|
||||
return fmt.Errorf("failed to get current value of reference %q: %w", ref.Ref, err)
|
||||
} else {
|
||||
oldValue = val
|
||||
}
|
||||
}
|
||||
|
||||
u.state = stateInitNew
|
||||
u.oldValue = oldValue
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *RefUpdater) InitNew(_ context.Context, newValue sha.SHA) error {
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if u.state != stateInitNew {
|
||||
return fmt.Errorf("invalid operation order: init new requires state=%s, current state=%s",
|
||||
stateInitNew, u.state)
|
||||
oldValueKnown = true
|
||||
}
|
||||
|
||||
if newValue.IsEmpty() {
|
||||
@ -172,8 +154,27 @@ func (u *RefUpdater) InitNew(_ context.Context, newValue sha.SHA) error {
|
||||
newValue = sha.Nil
|
||||
}
|
||||
|
||||
if oldValueKnown && oldValue == newValue {
|
||||
// skip the unchanged refs
|
||||
continue
|
||||
}
|
||||
|
||||
u.refs = append(u.refs, ReferenceUpdate{
|
||||
Ref: ref.Ref,
|
||||
Old: oldValue,
|
||||
New: newValue,
|
||||
})
|
||||
}
|
||||
|
||||
if len(refs) > 0 && len(u.refs) == 0 {
|
||||
return errors.New("updating zero references")
|
||||
}
|
||||
|
||||
sort.Slice(u.refs, func(i, j int) bool {
|
||||
return u.refs[i].Ref < u.refs[j].Ref
|
||||
})
|
||||
|
||||
u.state = statePre
|
||||
u.newValue = newValue
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -185,23 +186,13 @@ func (u *RefUpdater) Pre(ctx context.Context, alternateDirs ...string) error {
|
||||
statePre, u.state)
|
||||
}
|
||||
|
||||
// fail in case someone tries to delete a reference that doesn't exist.
|
||||
if u.oldValue.IsEmpty() && u.newValue.IsNil() {
|
||||
return errors.NotFound("reference %q not found", u.ref)
|
||||
}
|
||||
|
||||
if u.oldValue.IsNil() && u.newValue.IsNil() {
|
||||
return fmt.Errorf("provided values cannot be both empty")
|
||||
if len(u.refs) == 0 {
|
||||
u.state = stateUpdate
|
||||
return nil
|
||||
}
|
||||
|
||||
out, err := u.hookClient.PreReceive(ctx, PreReceiveInput{
|
||||
RefUpdates: []ReferenceUpdate{
|
||||
{
|
||||
Ref: u.ref,
|
||||
Old: u.oldValue,
|
||||
New: u.newValue,
|
||||
},
|
||||
},
|
||||
RefUpdates: u.refs,
|
||||
Environment: Environment{
|
||||
AlternateObjectDirs: alternateDirs,
|
||||
},
|
||||
@ -228,22 +219,33 @@ func (u *RefUpdater) UpdateRef(ctx context.Context) error {
|
||||
stateUpdate, u.state)
|
||||
}
|
||||
|
||||
cmd := command.New("update-ref")
|
||||
if u.newValue.IsNil() {
|
||||
cmd.Add(command.WithFlag("-d", u.ref))
|
||||
} else {
|
||||
cmd.Add(command.WithArg(u.ref, u.newValue.String()))
|
||||
if len(u.refs) == 0 {
|
||||
u.state = statePost
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd.Add(command.WithArg(u.oldValue.String()))
|
||||
input := bytes.NewBuffer(nil)
|
||||
for _, ref := range u.refs {
|
||||
switch {
|
||||
case ref.New.IsNil():
|
||||
_, _ = input.WriteString(fmt.Sprintf("delete %s\000%s\000", ref.Ref, ref.Old))
|
||||
case ref.Old.IsNil():
|
||||
_, _ = input.WriteString(fmt.Sprintf("create %s\000%s\000", ref.Ref, ref.New))
|
||||
default:
|
||||
_, _ = input.WriteString(fmt.Sprintf("update %s\000%s\000%s\000", ref.Ref, ref.New, ref.Old))
|
||||
}
|
||||
}
|
||||
|
||||
if err := cmd.Run(ctx, command.WithDir(u.repoPath)); err != nil {
|
||||
input.WriteString("commit\000")
|
||||
|
||||
cmd := command.New("update-ref", command.WithFlag("--stdin"), command.WithFlag("-z"))
|
||||
if err := cmd.Run(ctx, command.WithStdin(input), command.WithDir(u.repoPath)); err != nil {
|
||||
msg := err.Error()
|
||||
if strings.Contains(msg, "reference already exists") {
|
||||
return errors.Conflict("reference already exists")
|
||||
}
|
||||
|
||||
return fmt.Errorf("update of ref %q from %q to %q failed: %w", u.ref, u.oldValue, u.newValue, err)
|
||||
return fmt.Errorf("update of references %v failed: %w", u.refs, err)
|
||||
}
|
||||
|
||||
u.state = statePost
|
||||
@ -258,14 +260,13 @@ func (u *RefUpdater) Post(ctx context.Context, alternateDirs ...string) error {
|
||||
statePost, u.state)
|
||||
}
|
||||
|
||||
if len(u.refs) == 0 {
|
||||
u.state = stateDone
|
||||
return nil
|
||||
}
|
||||
|
||||
out, err := u.hookClient.PostReceive(ctx, PostReceiveInput{
|
||||
RefUpdates: []ReferenceUpdate{
|
||||
{
|
||||
Ref: u.ref,
|
||||
Old: u.oldValue,
|
||||
New: u.newValue,
|
||||
},
|
||||
},
|
||||
RefUpdates: u.refs,
|
||||
Environment: Environment{
|
||||
AlternateObjectDirs: alternateDirs,
|
||||
},
|
||||
@ -282,16 +283,16 @@ func (u *RefUpdater) Post(ctx context.Context, alternateDirs ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *RefUpdater) getRef(ctx context.Context) (sha.SHA, error) {
|
||||
func (u *RefUpdater) getRef(ctx context.Context, ref string) (sha.SHA, error) {
|
||||
cmd := command.New("show-ref",
|
||||
command.WithFlag("--verify"),
|
||||
command.WithFlag("-s"),
|
||||
command.WithArg(u.ref),
|
||||
command.WithArg(ref),
|
||||
)
|
||||
output := &bytes.Buffer{}
|
||||
err := cmd.Run(ctx, command.WithDir(u.repoPath), command.WithStdout(output))
|
||||
if cErr := command.AsError(err); cErr != nil && cErr.IsExitCode(128) && cErr.IsInvalidRefErr() {
|
||||
return sha.None, errors.NotFound("reference %q not found", u.ref)
|
||||
return sha.None, errors.NotFound("reference %q not found", ref)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
112
git/merge.go
112
git/merge.go
@ -26,6 +26,7 @@ import (
|
||||
"github.com/harness/gitness/git/merge"
|
||||
"github.com/harness/gitness/git/parser"
|
||||
"github.com/harness/gitness/git/sha"
|
||||
"github.com/harness/gitness/git/sharedrepo"
|
||||
)
|
||||
|
||||
// MergeParams is input structure object for merging operation.
|
||||
@ -56,8 +57,7 @@ type MergeParams struct {
|
||||
// (optional, default: committer date)
|
||||
AuthorDate *time.Time
|
||||
|
||||
RefType enum.RefType
|
||||
RefName string
|
||||
Refs []RefUpdate
|
||||
|
||||
// HeadExpectedSHA is commit sha on the head branch, if HeadExpectedSHA is older
|
||||
// than the HeadBranch latest sha then merge will fail.
|
||||
@ -69,6 +69,19 @@ type MergeParams struct {
|
||||
Method enum.MergeMethod
|
||||
}
|
||||
|
||||
type RefUpdate struct {
|
||||
// Name is the full name of the reference.
|
||||
Name string
|
||||
|
||||
// Old is the expected current value of the reference.
|
||||
// If it's empty, the old value of the reference can be any value.
|
||||
Old sha.SHA
|
||||
|
||||
// New is the desired value for the reference.
|
||||
// If it's empty, the reference would be set to the resulting commit SHA of the merge.
|
||||
New sha.SHA
|
||||
}
|
||||
|
||||
func (p *MergeParams) Validate() error {
|
||||
if err := p.WriteParams.Validate(); err != nil {
|
||||
return err
|
||||
@ -82,9 +95,12 @@ func (p *MergeParams) Validate() error {
|
||||
return errors.InvalidArgument("head branch is mandatory")
|
||||
}
|
||||
|
||||
if p.RefType != enum.RefTypeUndefined && p.RefName == "" {
|
||||
return errors.InvalidArgument("ref name has to be provided if type is defined")
|
||||
for _, ref := range p.Refs {
|
||||
if ref.Name == "" {
|
||||
return errors.InvalidArgument("ref name has to be provided")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -153,27 +169,6 @@ func (s *Service) Merge(ctx context.Context, params *MergeParams) (MergeOutput,
|
||||
panic("unsupported merge method")
|
||||
}
|
||||
|
||||
// set up the target reference
|
||||
|
||||
var refPath string
|
||||
var refOldValue sha.SHA
|
||||
|
||||
if params.RefType != enum.RefTypeUndefined {
|
||||
refPath, err = GetRefPath(params.RefName, params.RefType)
|
||||
if err != nil {
|
||||
return MergeOutput{}, fmt.Errorf(
|
||||
"failed to generate full reference for type '%s' and name '%s' for merge operation: %w",
|
||||
params.RefType, params.RefName, err)
|
||||
}
|
||||
|
||||
refOldValue, err = s.git.GetFullCommitID(ctx, repoPath, refPath)
|
||||
if errors.IsNotFound(err) {
|
||||
refOldValue = sha.Nil
|
||||
} else if err != nil {
|
||||
return MergeOutput{}, fmt.Errorf("failed to resolve %q: %w", refPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
// find the commit SHAs
|
||||
|
||||
baseCommitSHA := params.BaseSHA
|
||||
@ -259,35 +254,66 @@ func (s *Service) Merge(ctx context.Context, params *MergeParams) (MergeOutput,
|
||||
message = parser.CleanUpWhitespace(params.Message)
|
||||
}
|
||||
|
||||
// merge
|
||||
// create merge commit and update the references
|
||||
|
||||
var refUpdater *hook.RefUpdater
|
||||
|
||||
if params.RefType != enum.RefTypeUndefined {
|
||||
refUpdater, err = hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath, refPath)
|
||||
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath)
|
||||
if err != nil {
|
||||
return MergeOutput{}, errors.Internal(err, "failed to create ref updater object")
|
||||
return MergeOutput{}, fmt.Errorf("failed to create reference updater: %w", err)
|
||||
}
|
||||
|
||||
if err := refUpdater.InitOld(ctx, refOldValue); err != nil {
|
||||
return MergeOutput{}, errors.Internal(err, "failed to set old reference value for ref updater")
|
||||
}
|
||||
}
|
||||
var mergeCommitSHA sha.SHA
|
||||
var conflicts []string
|
||||
|
||||
mergeCommitSHA, conflicts, err := mergeFunc(
|
||||
err = sharedrepo.Run(ctx, refUpdater, s.tmpDir, repoPath, func(s *sharedrepo.SharedRepo) error {
|
||||
mergeCommitSHA, conflicts, err = mergeFunc(
|
||||
ctx,
|
||||
refUpdater,
|
||||
repoPath, s.tmpDir,
|
||||
&author, &committer,
|
||||
message,
|
||||
mergeBaseCommitSHA, baseCommitSHA, headCommitSHA)
|
||||
s,
|
||||
merge.Params{
|
||||
Author: &author,
|
||||
Committer: &committer,
|
||||
Message: message,
|
||||
MergeBaseSHA: mergeBaseCommitSHA,
|
||||
TargetSHA: baseCommitSHA,
|
||||
SourceSHA: headCommitSHA,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create merge commit: %w", err)
|
||||
}
|
||||
|
||||
if mergeCommitSHA.IsEmpty() || len(conflicts) > 0 {
|
||||
return refUpdater.Init(ctx, nil) // update nothing
|
||||
}
|
||||
|
||||
refUpdates := make([]hook.ReferenceUpdate, len(params.Refs))
|
||||
for i, ref := range params.Refs {
|
||||
oldValue := ref.Old
|
||||
newValue := ref.New
|
||||
|
||||
if newValue.IsEmpty() { // replace all empty new values to the result of the merge
|
||||
newValue = mergeCommitSHA
|
||||
}
|
||||
|
||||
refUpdates[i] = hook.ReferenceUpdate{
|
||||
Ref: ref.Name,
|
||||
Old: oldValue,
|
||||
New: newValue,
|
||||
}
|
||||
}
|
||||
|
||||
err = refUpdater.Init(ctx, refUpdates)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to init values of references (%v): %w", refUpdates, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if errors.IsConflict(err) {
|
||||
return MergeOutput{}, fmt.Errorf("failed to merge %q to %q in %q using the %q merge method: %w",
|
||||
params.HeadBranch, params.BaseBranch, params.RepoUID, mergeMethod, err)
|
||||
}
|
||||
if err != nil {
|
||||
return MergeOutput{}, errors.Internal(err, "failed to merge %q to %q in %q using the %q merge method",
|
||||
params.HeadBranch, params.BaseBranch, params.RepoUID, mergeMethod)
|
||||
return MergeOutput{}, fmt.Errorf("failed to merge %q to %q in %q using the %q merge method: %w",
|
||||
params.HeadBranch, params.BaseBranch, params.RepoUID, mergeMethod, err)
|
||||
}
|
||||
if len(conflicts) > 0 {
|
||||
return MergeOutput{
|
||||
|
@ -20,86 +20,62 @@ import (
|
||||
|
||||
"github.com/harness/gitness/errors"
|
||||
"github.com/harness/gitness/git/api"
|
||||
"github.com/harness/gitness/git/hook"
|
||||
"github.com/harness/gitness/git/sha"
|
||||
"github.com/harness/gitness/git/sharedrepo"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var (
|
||||
// errConflict is used to error out of sharedrepo Run method without erroring out of merge in case of conflicts.
|
||||
errConflict = errors.New("conflict")
|
||||
)
|
||||
type Params struct {
|
||||
Author, Committer *api.Signature
|
||||
Message string
|
||||
MergeBaseSHA, TargetSHA, SourceSHA sha.SHA
|
||||
}
|
||||
|
||||
// Func represents a merge method function. The concrete merge implementation functions must have this signature.
|
||||
type Func func(
|
||||
ctx context.Context,
|
||||
refUpdater *hook.RefUpdater,
|
||||
repoPath, tmpDir string,
|
||||
author, committer *api.Signature,
|
||||
message string,
|
||||
mergeBaseSHA, targetSHA, sourceSHA sha.SHA,
|
||||
s *sharedrepo.SharedRepo,
|
||||
params Params,
|
||||
) (mergeSHA sha.SHA, conflicts []string, err error)
|
||||
|
||||
// Merge merges two the commits (targetSHA and sourceSHA) using the Merge method.
|
||||
func Merge(
|
||||
ctx context.Context,
|
||||
refUpdater *hook.RefUpdater,
|
||||
repoPath, tmpDir string,
|
||||
author, committer *api.Signature,
|
||||
message string,
|
||||
mergeBaseSHA, targetSHA, sourceSHA sha.SHA,
|
||||
s *sharedrepo.SharedRepo,
|
||||
params Params,
|
||||
) (mergeSHA sha.SHA, conflicts []string, err error) {
|
||||
return mergeInternal(ctx,
|
||||
refUpdater,
|
||||
repoPath, tmpDir,
|
||||
author, committer,
|
||||
message,
|
||||
mergeBaseSHA, targetSHA, sourceSHA,
|
||||
false)
|
||||
return mergeInternal(ctx, s, params, false)
|
||||
}
|
||||
|
||||
// Squash merges two the commits (targetSHA and sourceSHA) using the Squash method.
|
||||
func Squash(
|
||||
ctx context.Context,
|
||||
refUpdater *hook.RefUpdater,
|
||||
repoPath, tmpDir string,
|
||||
author, committer *api.Signature,
|
||||
message string,
|
||||
mergeBaseSHA, targetSHA, sourceSHA sha.SHA,
|
||||
s *sharedrepo.SharedRepo,
|
||||
params Params,
|
||||
) (mergeSHA sha.SHA, conflicts []string, err error) {
|
||||
return mergeInternal(ctx,
|
||||
refUpdater,
|
||||
repoPath, tmpDir,
|
||||
author, committer,
|
||||
message,
|
||||
mergeBaseSHA, targetSHA, sourceSHA,
|
||||
true)
|
||||
return mergeInternal(ctx, s, params, true)
|
||||
}
|
||||
|
||||
// mergeInternal is internal implementation of merge used for Merge and Squash methods.
|
||||
func mergeInternal(
|
||||
ctx context.Context,
|
||||
refUpdater *hook.RefUpdater,
|
||||
repoPath, tmpDir string,
|
||||
author, committer *api.Signature,
|
||||
message string,
|
||||
mergeBaseSHA, targetSHA, sourceSHA sha.SHA,
|
||||
func mergeInternal(ctx context.Context,
|
||||
s *sharedrepo.SharedRepo,
|
||||
params Params,
|
||||
squash bool,
|
||||
) (mergeSHA sha.SHA, conflicts []string, err error) {
|
||||
err = sharedrepo.Run(ctx, refUpdater, tmpDir, repoPath, func(s *sharedrepo.SharedRepo) error {
|
||||
var err error
|
||||
mergeBaseSHA := params.MergeBaseSHA
|
||||
targetSHA := params.TargetSHA
|
||||
sourceSHA := params.SourceSHA
|
||||
|
||||
var treeSHA sha.SHA
|
||||
|
||||
treeSHA, conflicts, err = s.MergeTree(ctx, mergeBaseSHA, targetSHA, sourceSHA)
|
||||
if err != nil {
|
||||
return fmt.Errorf("merge tree failed: %w", err)
|
||||
return sha.None, nil, fmt.Errorf("merge tree failed: %w", err)
|
||||
}
|
||||
|
||||
if len(conflicts) > 0 {
|
||||
return errConflict
|
||||
return sha.None, conflicts, nil
|
||||
}
|
||||
|
||||
parents := make([]sha.SHA, 0, 2)
|
||||
@ -108,45 +84,37 @@ func mergeInternal(
|
||||
parents = append(parents, sourceSHA)
|
||||
}
|
||||
|
||||
mergeSHA, err = s.CommitTree(ctx, author, committer, treeSHA, message, false, parents...)
|
||||
mergeSHA, err = s.CommitTree(ctx, params.Author, params.Committer, treeSHA, params.Message, false, parents...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("commit tree failed: %w", err)
|
||||
}
|
||||
|
||||
if err := refUpdater.InitNew(ctx, mergeSHA); err != nil {
|
||||
return fmt.Errorf("refUpdater.InitNew failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil && !errors.Is(err, errConflict) {
|
||||
return sha.None, nil, fmt.Errorf("merge method=merge squash=%t: %w", squash, err)
|
||||
return sha.None, nil, fmt.Errorf("commit tree failed: %w", err)
|
||||
}
|
||||
|
||||
return mergeSHA, conflicts, nil
|
||||
}
|
||||
|
||||
// Rebase merges two the commits (targetSHA and sourceSHA) using the Rebase method.
|
||||
// Commit author isn't used here - it's copied from every commit.
|
||||
// Commit message isn't used here
|
||||
//
|
||||
//nolint:gocognit // refactor if needed.
|
||||
func Rebase(
|
||||
ctx context.Context,
|
||||
refUpdater *hook.RefUpdater,
|
||||
repoPath, tmpDir string,
|
||||
_, committer *api.Signature, // commit author isn't used here - it's copied from every commit
|
||||
_ string, // commit message isn't used here
|
||||
mergeBaseSHA, targetSHA, sourceSHA sha.SHA,
|
||||
s *sharedrepo.SharedRepo,
|
||||
params Params,
|
||||
) (mergeSHA sha.SHA, conflicts []string, err error) {
|
||||
err = sharedrepo.Run(ctx, refUpdater, tmpDir, repoPath, func(s *sharedrepo.SharedRepo) error {
|
||||
mergeBaseSHA := params.MergeBaseSHA
|
||||
targetSHA := params.TargetSHA
|
||||
sourceSHA := params.SourceSHA
|
||||
|
||||
sourceSHAs, err := s.CommitSHAsForRebase(ctx, mergeBaseSHA, sourceSHA)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find commit list in rebase merge: %w", err)
|
||||
return sha.None, nil, fmt.Errorf("failed to find commit list in rebase merge: %w", err)
|
||||
}
|
||||
|
||||
lastCommitSHA := targetSHA
|
||||
lastTreeSHA, err := s.GetTreeSHA(ctx, targetSHA.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get tree sha for target: %w", err)
|
||||
return sha.None, nil, fmt.Errorf("failed to get tree sha for target: %w", err)
|
||||
}
|
||||
|
||||
for _, commitSHA := range sourceSHAs {
|
||||
@ -154,7 +122,7 @@ func Rebase(
|
||||
|
||||
commitInfo, err := api.GetCommit(ctx, s.Directory(), commitSHA.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get commit data in rebase merge: %w", err)
|
||||
return sha.None, nil, fmt.Errorf("failed to get commit data in rebase merge: %w", err)
|
||||
}
|
||||
|
||||
// rebase merge preserves the commit author (and date) and the commit message, but changes the committer.
|
||||
@ -176,10 +144,10 @@ func Rebase(
|
||||
|
||||
treeSHA, conflicts, err = s.MergeTree(ctx, mergeTreeMergeBaseSHA, lastCommitSHA, commitSHA)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to merge tree in rebase merge: %w", err)
|
||||
return sha.None, nil, fmt.Errorf("failed to merge tree in rebase merge: %w", err)
|
||||
}
|
||||
if len(conflicts) > 0 {
|
||||
return errConflict
|
||||
return sha.None, conflicts, nil
|
||||
}
|
||||
|
||||
// Drop any commit which after being rebased would be empty.
|
||||
@ -194,48 +162,33 @@ func Rebase(
|
||||
continue
|
||||
}
|
||||
|
||||
lastCommitSHA, err = s.CommitTree(ctx, author, committer, treeSHA, message, false, lastCommitSHA)
|
||||
lastCommitSHA, err = s.CommitTree(ctx, author, params.Committer, treeSHA, message, false, lastCommitSHA)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to commit tree in rebase merge: %w", err)
|
||||
return sha.None, nil, fmt.Errorf("failed to commit tree in rebase merge: %w", err)
|
||||
}
|
||||
lastTreeSHA = treeSHA
|
||||
}
|
||||
|
||||
if err := refUpdater.InitNew(ctx, lastCommitSHA); err != nil {
|
||||
return fmt.Errorf("refUpdater.InitNew failed: %w", err)
|
||||
}
|
||||
|
||||
mergeSHA = lastCommitSHA
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil && !errors.Is(err, errConflict) {
|
||||
return sha.None, nil, fmt.Errorf("merge method=rebase: %w", err)
|
||||
}
|
||||
|
||||
return mergeSHA, conflicts, nil
|
||||
return mergeSHA, nil, nil
|
||||
}
|
||||
|
||||
// FastForward points the is internal implementation of merge used for Merge and Squash methods.
|
||||
// Commit author and committer aren't used here. Commit message isn't used here.
|
||||
func FastForward(
|
||||
ctx context.Context,
|
||||
refUpdater *hook.RefUpdater,
|
||||
repoPath, tmpDir string,
|
||||
_, _ *api.Signature, // commit author and committer aren't used here
|
||||
_ string, // commit message isn't used here
|
||||
mergeBaseSHA, targetSHA, sourceSHA sha.SHA,
|
||||
_ context.Context,
|
||||
_ *sharedrepo.SharedRepo,
|
||||
params Params,
|
||||
) (mergeSHA sha.SHA, conflicts []string, err error) {
|
||||
mergeBaseSHA := params.MergeBaseSHA
|
||||
targetSHA := params.TargetSHA
|
||||
sourceSHA := params.SourceSHA
|
||||
|
||||
if targetSHA != mergeBaseSHA {
|
||||
return sha.None, nil,
|
||||
errors.Conflict("Target branch has diverged from the source branch. Fast-forward not possible.")
|
||||
}
|
||||
|
||||
err = sharedrepo.Run(ctx, refUpdater, tmpDir, repoPath, func(*sharedrepo.SharedRepo) error {
|
||||
return refUpdater.InitNew(ctx, sourceSHA)
|
||||
})
|
||||
if err != nil {
|
||||
return sha.None, nil, fmt.Errorf("merge method=fast-forward: %w", err)
|
||||
}
|
||||
|
||||
return sourceSHA, nil, nil
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ func (s *Service) CommitFiles(ctx context.Context, params *CommitFilesParams) (C
|
||||
refOldSHA = commit.SHA
|
||||
}
|
||||
|
||||
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath, branchRef)
|
||||
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath)
|
||||
if err != nil {
|
||||
return CommitFilesResponse{}, fmt.Errorf("failed to create ref updater: %w", err)
|
||||
}
|
||||
@ -222,7 +222,13 @@ func (s *Service) CommitFiles(ctx context.Context, params *CommitFilesParams) (C
|
||||
|
||||
refNewSHA = commitSHA
|
||||
|
||||
if err := refUpdater.Init(ctx, refOldSHA, refNewSHA); err != nil {
|
||||
ref := hook.ReferenceUpdate{
|
||||
Ref: branchRef,
|
||||
Old: refOldSHA,
|
||||
New: refNewSHA,
|
||||
}
|
||||
|
||||
if err := refUpdater.Init(ctx, []hook.ReferenceUpdate{ref}); err != nil {
|
||||
return fmt.Errorf("failed to init ref updater old=%s new=%s: %w", refOldSHA, refNewSHA, err)
|
||||
}
|
||||
|
||||
|
10
git/ref.go
10
git/ref.go
@ -89,15 +89,15 @@ func (s *Service) UpdateRef(ctx context.Context, params UpdateRefParams) error {
|
||||
|
||||
reference, err := GetRefPath(params.Name, params.Type)
|
||||
if err != nil {
|
||||
return fmt.Errorf("UpdateRef: failed to fetch reference '%s': %w", params.Name, err)
|
||||
return fmt.Errorf("failed to create reference '%s': %w", params.Name, err)
|
||||
}
|
||||
|
||||
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath, reference)
|
||||
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("UpdateRef: failed to create ref updater: %w", err)
|
||||
return fmt.Errorf("failed to create ref updater: %w", err)
|
||||
}
|
||||
|
||||
if err := refUpdater.Do(ctx, params.OldValue, params.NewValue); err != nil {
|
||||
if err := refUpdater.DoOne(ctx, reference, params.OldValue, params.NewValue); err != nil {
|
||||
return fmt.Errorf("failed to update ref: %w", err)
|
||||
}
|
||||
|
||||
@ -122,8 +122,6 @@ func GetRefPath(refName string, refType enum.RefType) (string, error) {
|
||||
return refPullReqPrefix + refName + refPullReqHeadSuffix, nil
|
||||
case enum.RefTypePullReqMerge:
|
||||
return refPullReqPrefix + refName + refPullReqMergeSuffix, nil
|
||||
case enum.RefTypeUndefined:
|
||||
fallthrough
|
||||
default:
|
||||
return "", errors.InvalidArgument("provided reference type '%s' is invalid", refType)
|
||||
}
|
||||
|
17
git/tag.go
17
git/tag.go
@ -278,7 +278,7 @@ func (s *Service) CreateCommitTag(ctx context.Context, params *CreateCommitTagPa
|
||||
|
||||
// ref updater
|
||||
|
||||
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath, tagRef)
|
||||
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create ref updater to create the tag: %w", err)
|
||||
}
|
||||
@ -295,7 +295,13 @@ func (s *Service) CreateCommitTag(ctx context.Context, params *CreateCommitTagPa
|
||||
return fmt.Errorf("failed to read annotated tag after creation: %w", err)
|
||||
}
|
||||
|
||||
if err := refUpdater.Init(ctx, sha.Nil, tag.Sha); err != nil {
|
||||
ref := hook.ReferenceUpdate{
|
||||
Ref: tagRef,
|
||||
Old: sha.Nil,
|
||||
New: tag.Sha,
|
||||
}
|
||||
|
||||
if err := refUpdater.Init(ctx, []hook.ReferenceUpdate{ref}); err != nil {
|
||||
return fmt.Errorf("failed to init ref updater: %w", err)
|
||||
}
|
||||
|
||||
@ -337,14 +343,15 @@ func (s *Service) DeleteTag(ctx context.Context, params *DeleteTagParams) error
|
||||
}
|
||||
|
||||
repoPath := getFullPathForRepo(s.reposRoot, params.RepoUID)
|
||||
tagRef := api.GetReferenceFromTagName(params.Name)
|
||||
|
||||
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath, tagRef)
|
||||
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create ref updater to delete the tag: %w", err)
|
||||
}
|
||||
|
||||
err = refUpdater.Do(ctx, sha.None, sha.Nil) // delete whatever is there
|
||||
tagRef := api.GetReferenceFromTagName(params.Name)
|
||||
|
||||
err = refUpdater.DoOne(ctx, tagRef, sha.None, sha.Nil) // delete whatever is there
|
||||
if errors.IsNotFound(err) {
|
||||
return errors.NotFound("tag %q does not exist", params.Name)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user