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
|
sourceRepo := targetRepo
|
||||||
sourceWriteParams := targetWriteParams
|
|
||||||
if pr.SourceRepoID != pr.TargetRepoID {
|
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)
|
sourceRepo, err = c.repoStore.Find(ctx, pr.SourceRepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to get source repository: %w", err)
|
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)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to list status checks: %w", err)
|
return nil, nil, fmt.Errorf("failed to list status checks: %w", err)
|
||||||
}
|
}
|
||||||
@ -292,7 +286,7 @@ func (c *Controller) Merge(
|
|||||||
BaseBranch: pr.TargetBranch,
|
BaseBranch: pr.TargetBranch,
|
||||||
HeadRepoUID: sourceRepo.GitUID,
|
HeadRepoUID: sourceRepo.GitUID,
|
||||||
HeadBranch: pr.SourceBranch,
|
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),
|
HeadExpectedSHA: sha.Must(in.SourceSHA),
|
||||||
Method: gitenum.MergeMethod(in.Method),
|
Method: gitenum.MergeMethod(in.Method),
|
||||||
})
|
})
|
||||||
@ -425,6 +419,65 @@ func (c *Controller) Merge(
|
|||||||
|
|
||||||
log.Ctx(ctx).Debug().Msgf("all pre-check passed, merge PR")
|
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()
|
now := time.Now()
|
||||||
mergeOutput, err := c.git.Merge(ctx, &git.MergeParams{
|
mergeOutput, err := c.git.Merge(ctx, &git.MergeParams{
|
||||||
WriteParams: targetWriteParams,
|
WriteParams: targetWriteParams,
|
||||||
@ -436,8 +489,7 @@ func (c *Controller) Merge(
|
|||||||
CommitterDate: &now,
|
CommitterDate: &now,
|
||||||
Author: author,
|
Author: author,
|
||||||
AuthorDate: &now,
|
AuthorDate: &now,
|
||||||
RefType: gitenum.RefTypeBranch,
|
Refs: refUpdates,
|
||||||
RefName: pr.TargetBranch,
|
|
||||||
HeadExpectedSHA: sha.Must(in.SourceSHA),
|
HeadExpectedSHA: sha.Must(in.SourceSHA),
|
||||||
Method: gitenum.MergeMethod(in.Method),
|
Method: gitenum.MergeMethod(in.Method),
|
||||||
})
|
})
|
||||||
@ -545,27 +597,13 @@ func (c *Controller) Merge(
|
|||||||
SourceSHA: mergeOutput.HeadSHA.String(),
|
SourceSHA: mergeOutput.HeadSHA.String(),
|
||||||
})
|
})
|
||||||
|
|
||||||
var branchDeleted bool
|
|
||||||
if ruleOut.DeleteSourceBranch {
|
if ruleOut.DeleteSourceBranch {
|
||||||
errDelete := c.git.DeleteBranch(ctx, &git.DeleteBranchParams{
|
pr.ActivitySeq = activitySeqBranchDeleted
|
||||||
WriteParams: sourceWriteParams,
|
if _, errAct := c.activityStore.CreateWithPayload(ctx, pr, mergedBy,
|
||||||
BranchName: pr.SourceBranch,
|
&types.PullRequestActivityPayloadBranchDelete{SHA: in.SourceSHA}, nil); errAct != nil {
|
||||||
})
|
|
||||||
if errDelete != nil {
|
|
||||||
// non-critical error
|
// non-critical error
|
||||||
log.Ctx(ctx).Err(errDelete).Msgf("failed to delete source branch after merging")
|
log.Ctx(ctx).Err(errAct).
|
||||||
} else {
|
Msgf("failed to write pull request activity for successful automatic branch delete")
|
||||||
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 {
|
|
||||||
// non-critical error
|
|
||||||
log.Ctx(ctx).Err(errAct).
|
|
||||||
Msgf("failed to write pull request activity for successful automatic branch delete")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -621,7 +659,7 @@ func (c *Controller) Merge(
|
|||||||
}
|
}
|
||||||
return &types.MergeResponse{
|
return &types.MergeResponse{
|
||||||
SHA: mergeOutput.MergeSHA.String(),
|
SHA: mergeOutput.MergeSHA.String(),
|
||||||
BranchDeleted: branchDeleted,
|
BranchDeleted: ruleOut.DeleteSourceBranch,
|
||||||
RuleViolations: violations,
|
RuleViolations: violations,
|
||||||
}, nil, nil
|
}, nil, nil
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,7 @@ func (c *Controller) Create(
|
|||||||
Name: strconv.FormatInt(targetRepo.PullReqSeq, 10),
|
Name: strconv.FormatInt(targetRepo.PullReqSeq, 10),
|
||||||
Type: gitenum.RefTypePullReqHead,
|
Type: gitenum.RefTypePullReqHead,
|
||||||
NewValue: sourceSHA,
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create PR head ref: %w", err)
|
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.Closed = &nowMilli
|
||||||
pr.MarkAsMergeUnchecked()
|
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:
|
case changeReopen:
|
||||||
pr.SourceSHA = sourceSHA.String()
|
pr.SourceSHA = sourceSHA.String()
|
||||||
pr.MergeBaseSHA = mergeBaseSHA.String()
|
pr.MergeBaseSHA = mergeBaseSHA.String()
|
||||||
pr.Closed = nil
|
pr.Closed = nil
|
||||||
|
|
||||||
|
// create the head pull request reference
|
||||||
err = c.git.UpdateRef(ctx, git.UpdateRefParams{
|
err = c.git.UpdateRef(ctx, git.UpdateRefParams{
|
||||||
WriteParams: targetWriteParams,
|
WriteParams: targetWriteParams,
|
||||||
Name: strconv.FormatInt(pr.Number, 10),
|
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)
|
return nil, nil, fmt.Errorf("failed to create RPC write params: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
refType := gitenum.RefTypeBranch
|
var refs []git.RefUpdate
|
||||||
refName := in.HeadBranch
|
if !in.DryRun {
|
||||||
if in.DryRun {
|
headBranchRef, err := git.GetRefPath(in.HeadBranch, gitenum.RefTypeBranch)
|
||||||
refType = gitenum.RefTypeUndefined
|
if err != nil {
|
||||||
refName = ""
|
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{
|
mergeOutput, err := c.git.Merge(ctx, &git.MergeParams{
|
||||||
@ -169,8 +176,7 @@ func (c *Controller) Rebase(
|
|||||||
BaseSHA: baseCommitSHA,
|
BaseSHA: baseCommitSHA,
|
||||||
HeadRepoUID: repo.GitUID,
|
HeadRepoUID: repo.GitUID,
|
||||||
HeadBranch: in.HeadBranch,
|
HeadBranch: in.HeadBranch,
|
||||||
RefType: refType,
|
Refs: refs,
|
||||||
RefName: refName,
|
|
||||||
HeadExpectedSHA: in.HeadCommitSHA,
|
HeadExpectedSHA: in.HeadCommitSHA,
|
||||||
Method: gitenum.MergeMethodRebase,
|
Method: gitenum.MergeMethodRebase,
|
||||||
})
|
})
|
||||||
|
@ -135,6 +135,11 @@ func (c *Controller) Squash(
|
|||||||
in.HeadCommitSHA, headBranch.Branch.Name)
|
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
|
baseCommitSHA := in.BaseCommitSHA
|
||||||
if baseCommitSHA.IsEmpty() {
|
if baseCommitSHA.IsEmpty() {
|
||||||
baseBranch, err := c.git.GetBranch(ctx, &git.GetBranchParams{
|
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)
|
return nil, nil, fmt.Errorf("failed to create RPC write params: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
refType := gitenum.RefTypeBranch
|
var refs []git.RefUpdate
|
||||||
refName := in.HeadBranch
|
if !in.DryRun {
|
||||||
if in.DryRun {
|
refs = append(refs, git.RefUpdate{
|
||||||
refType = gitenum.RefTypeUndefined
|
Name: headBranchRef,
|
||||||
refName = ""
|
Old: headBranch.Branch.SHA,
|
||||||
|
New: sha.SHA{}, // update to the result of the merge
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
mergeBase, err := c.git.MergeBase(ctx, git.MergeBaseParams{
|
mergeBase, err := c.git.MergeBase(ctx, git.MergeBaseParams{
|
||||||
@ -223,8 +230,7 @@ func (c *Controller) Squash(
|
|||||||
HeadRepoUID: repo.GitUID,
|
HeadRepoUID: repo.GitUID,
|
||||||
HeadBranch: in.HeadBranch,
|
HeadBranch: in.HeadBranch,
|
||||||
Message: git.CommitMessage(in.Title, in.Message),
|
Message: git.CommitMessage(in.Title, in.Message),
|
||||||
RefType: refType,
|
Refs: refs,
|
||||||
RefName: refName,
|
|
||||||
Committer: committer,
|
Committer: committer,
|
||||||
CommitterDate: &now,
|
CommitterDate: &now,
|
||||||
Author: author,
|
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.
|
//nolint:funlen // refactor if required.
|
||||||
func (s *Service) updateMergeData(
|
func (s *Service) updateMergeData(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
@ -186,6 +146,19 @@ func (s *Service) updateMergeData(
|
|||||||
return fmt.Errorf("failed to generate rpc write params: %w", err)
|
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.
|
// call merge and store output in pr merge reference.
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
mergeOutput, err := s.git.Merge(ctx, &git.MergeParams{
|
mergeOutput, err := s.git.Merge(ctx, &git.MergeParams{
|
||||||
@ -193,8 +166,7 @@ func (s *Service) updateMergeData(
|
|||||||
BaseBranch: pr.TargetBranch,
|
BaseBranch: pr.TargetBranch,
|
||||||
HeadRepoUID: sourceRepo.GitUID,
|
HeadRepoUID: sourceRepo.GitUID,
|
||||||
HeadBranch: pr.SourceBranch,
|
HeadBranch: pr.SourceBranch,
|
||||||
RefType: gitenum.RefTypePullReqMerge,
|
Refs: refs,
|
||||||
RefName: strconv.Itoa(int(pr.Number)),
|
|
||||||
HeadExpectedSHA: sha.Must(newSHA),
|
HeadExpectedSHA: sha.Must(newSHA),
|
||||||
Force: true,
|
Force: true,
|
||||||
|
|
||||||
|
@ -194,8 +194,6 @@ func New(ctx context.Context,
|
|||||||
_ = r.RegisterCreated(service.mergeCheckOnCreated)
|
_ = r.RegisterCreated(service.mergeCheckOnCreated)
|
||||||
_ = r.RegisterBranchUpdated(service.mergeCheckOnBranchUpdate)
|
_ = r.RegisterBranchUpdated(service.mergeCheckOnBranchUpdate)
|
||||||
_ = r.RegisterReopened(service.mergeCheckOnReopen)
|
_ = r.RegisterReopened(service.mergeCheckOnReopen)
|
||||||
_ = r.RegisterClosed(service.mergeCheckOnClosed)
|
|
||||||
_ = r.RegisterMerged(service.mergeCheckOnMerged)
|
|
||||||
|
|
||||||
return nil
|
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)
|
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)
|
||||||
|
|
||||||
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath, branchRef)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create ref updater to create the branch: %w", err)
|
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) {
|
if errors.IsConflict(err) {
|
||||||
return nil, errors.Conflict("branch %q already exists", params.BranchName)
|
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)
|
repoPath := getFullPathForRepo(s.reposRoot, params.RepoUID)
|
||||||
branchRef := api.GetReferenceFromBranchName(params.BranchName)
|
|
||||||
commitSha, _ := sha.NewOrEmpty(params.SHA)
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create ref updater to create the branch: %w", err)
|
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) {
|
if errors.IsNotFound(err) {
|
||||||
return errors.NotFound("branch %q does not exist", params.BranchName)
|
return errors.NotFound("branch %q does not exist", params.BranchName)
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,7 @@ package enum
|
|||||||
type RefType int
|
type RefType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RefTypeUndefined RefType = iota
|
RefTypeRaw RefType = iota
|
||||||
RefTypeRaw
|
|
||||||
RefTypeBranch
|
RefTypeBranch
|
||||||
RefTypeTag
|
RefTypeTag
|
||||||
RefTypePullReqHead
|
RefTypePullReqHead
|
||||||
@ -37,8 +36,6 @@ func (t RefType) String() string {
|
|||||||
return "head"
|
return "head"
|
||||||
case RefTypePullReqMerge:
|
case RefTypePullReqMerge:
|
||||||
return "merge"
|
return "merge"
|
||||||
case RefTypeUndefined:
|
|
||||||
fallthrough
|
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/harness/gitness/errors"
|
"github.com/harness/gitness/errors"
|
||||||
@ -32,7 +33,6 @@ func CreateRefUpdater(
|
|||||||
hookClientFactory ClientFactory,
|
hookClientFactory ClientFactory,
|
||||||
envVars map[string]string,
|
envVars map[string]string,
|
||||||
repoPath string,
|
repoPath string,
|
||||||
ref string,
|
|
||||||
) (*RefUpdater, error) {
|
) (*RefUpdater, error) {
|
||||||
if repoPath == "" {
|
if repoPath == "" {
|
||||||
return nil, errors.Internal(nil, "repo path can't be empty")
|
return nil, errors.Internal(nil, "repo path can't be empty")
|
||||||
@ -44,13 +44,11 @@ func CreateRefUpdater(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &RefUpdater{
|
return &RefUpdater{
|
||||||
state: stateInitOld,
|
state: stateInit,
|
||||||
hookClient: client,
|
hookClient: client,
|
||||||
envVars: envVars,
|
envVars: envVars,
|
||||||
repoPath: repoPath,
|
repoPath: repoPath,
|
||||||
ref: ref,
|
refs: nil,
|
||||||
oldValue: sha.None,
|
|
||||||
newValue: sha.None,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,9 +60,7 @@ type RefUpdater struct {
|
|||||||
hookClient Client
|
hookClient Client
|
||||||
envVars map[string]string
|
envVars map[string]string
|
||||||
repoPath string
|
repoPath string
|
||||||
ref string
|
refs []ReferenceUpdate
|
||||||
oldValue sha.SHA
|
|
||||||
newValue sha.SHA
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// refUpdaterState represents state of the ref updater internal state machine.
|
// refUpdaterState represents state of the ref updater internal state machine.
|
||||||
@ -72,10 +68,8 @@ type refUpdaterState byte
|
|||||||
|
|
||||||
func (t refUpdaterState) String() string {
|
func (t refUpdaterState) String() string {
|
||||||
switch t {
|
switch t {
|
||||||
case stateInitOld:
|
case stateInit:
|
||||||
return "INIT_OLD"
|
return "INIT"
|
||||||
case stateInitNew:
|
|
||||||
return "INIT_NEW"
|
|
||||||
case statePre:
|
case statePre:
|
||||||
return "PRE"
|
return "PRE"
|
||||||
case stateUpdate:
|
case stateUpdate:
|
||||||
@ -89,8 +83,7 @@ func (t refUpdaterState) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
stateInitOld refUpdaterState = iota
|
stateInit refUpdaterState = iota
|
||||||
stateInitNew
|
|
||||||
statePre
|
statePre
|
||||||
stateUpdate
|
stateUpdate
|
||||||
statePost
|
statePost
|
||||||
@ -98,8 +91,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Do runs full ref update by executing all methods in the correct order.
|
// 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 {
|
func (u *RefUpdater) Do(ctx context.Context, refs []ReferenceUpdate) error {
|
||||||
if err := u.Init(ctx, oldValue, newValue); err != nil {
|
if err := u.Init(ctx, refs); err != nil {
|
||||||
return fmt.Errorf("init failed: %w", err)
|
return fmt.Errorf("init failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,62 +111,70 @@ func (u *RefUpdater) Do(ctx context.Context, oldValue, newValue sha.SHA) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *RefUpdater) Init(ctx context.Context, oldValue, newValue sha.SHA) error {
|
// DoOne runs full ref update of only one reference.
|
||||||
if err := u.InitOld(ctx, oldValue); err != nil {
|
func (u *RefUpdater) DoOne(ctx context.Context, ref string, oldValue, newValue sha.SHA) error {
|
||||||
return fmt.Errorf("init old failed: %w", err)
|
return u.Do(ctx, []ReferenceUpdate{
|
||||||
}
|
{
|
||||||
if err := u.InitNew(ctx, newValue); err != nil {
|
Ref: ref,
|
||||||
return fmt.Errorf("init new failed: %w", err)
|
Old: oldValue,
|
||||||
}
|
New: newValue,
|
||||||
|
},
|
||||||
return nil
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *RefUpdater) InitOld(ctx context.Context, oldValue sha.SHA) error {
|
func (u *RefUpdater) Init(ctx context.Context, refs []ReferenceUpdate) error {
|
||||||
if u == nil {
|
if u.state != stateInit {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.state != stateInitOld {
|
|
||||||
return fmt.Errorf("invalid operation order: init old requires state=%s, current state=%s",
|
return fmt.Errorf("invalid operation order: init old requires state=%s, current state=%s",
|
||||||
stateInitOld, u.state)
|
stateInit, u.state)
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldValue.IsEmpty() {
|
u.refs = make([]ReferenceUpdate, 0, len(refs))
|
||||||
// if no old value was provided, use current value (as required for hooks)
|
for _, ref := range refs {
|
||||||
val, err := u.getRef(ctx)
|
oldValue := ref.Old
|
||||||
if errors.IsNotFound(err) { //nolint:gocritic
|
newValue := ref.New
|
||||||
oldValue = sha.Nil
|
|
||||||
} else if err != nil {
|
var oldValueKnown bool
|
||||||
return fmt.Errorf("failed to get current value of reference: %w", err)
|
|
||||||
} else {
|
if oldValue.IsEmpty() {
|
||||||
oldValue = val
|
// if no old value was provided, use current value (as required for hooks)
|
||||||
|
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 %q: %w", ref.Ref, err)
|
||||||
|
} else {
|
||||||
|
oldValue = val
|
||||||
|
}
|
||||||
|
|
||||||
|
oldValueKnown = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if newValue.IsEmpty() {
|
||||||
|
// don't break existing interface - user calls with empty value to delete the ref.
|
||||||
|
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,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
u.state = stateInitNew
|
if len(refs) > 0 && len(u.refs) == 0 {
|
||||||
u.oldValue = oldValue
|
return errors.New("updating zero references")
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *RefUpdater) InitNew(_ context.Context, newValue sha.SHA) error {
|
|
||||||
if u == nil {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.state != stateInitNew {
|
sort.Slice(u.refs, func(i, j int) bool {
|
||||||
return fmt.Errorf("invalid operation order: init new requires state=%s, current state=%s",
|
return u.refs[i].Ref < u.refs[j].Ref
|
||||||
stateInitNew, u.state)
|
})
|
||||||
}
|
|
||||||
|
|
||||||
if newValue.IsEmpty() {
|
|
||||||
// don't break existing interface - user calls with empty value to delete the ref.
|
|
||||||
newValue = sha.Nil
|
|
||||||
}
|
|
||||||
|
|
||||||
u.state = statePre
|
u.state = statePre
|
||||||
u.newValue = newValue
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -185,23 +186,13 @@ func (u *RefUpdater) Pre(ctx context.Context, alternateDirs ...string) error {
|
|||||||
statePre, u.state)
|
statePre, u.state)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fail in case someone tries to delete a reference that doesn't exist.
|
if len(u.refs) == 0 {
|
||||||
if u.oldValue.IsEmpty() && u.newValue.IsNil() {
|
u.state = stateUpdate
|
||||||
return errors.NotFound("reference %q not found", u.ref)
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
if u.oldValue.IsNil() && u.newValue.IsNil() {
|
|
||||||
return fmt.Errorf("provided values cannot be both empty")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := u.hookClient.PreReceive(ctx, PreReceiveInput{
|
out, err := u.hookClient.PreReceive(ctx, PreReceiveInput{
|
||||||
RefUpdates: []ReferenceUpdate{
|
RefUpdates: u.refs,
|
||||||
{
|
|
||||||
Ref: u.ref,
|
|
||||||
Old: u.oldValue,
|
|
||||||
New: u.newValue,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Environment: Environment{
|
Environment: Environment{
|
||||||
AlternateObjectDirs: alternateDirs,
|
AlternateObjectDirs: alternateDirs,
|
||||||
},
|
},
|
||||||
@ -228,22 +219,33 @@ func (u *RefUpdater) UpdateRef(ctx context.Context) error {
|
|||||||
stateUpdate, u.state)
|
stateUpdate, u.state)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := command.New("update-ref")
|
if len(u.refs) == 0 {
|
||||||
if u.newValue.IsNil() {
|
u.state = statePost
|
||||||
cmd.Add(command.WithFlag("-d", u.ref))
|
return nil
|
||||||
} else {
|
|
||||||
cmd.Add(command.WithArg(u.ref, u.newValue.String()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
msg := err.Error()
|
||||||
if strings.Contains(msg, "reference already exists") {
|
if strings.Contains(msg, "reference already exists") {
|
||||||
return errors.Conflict("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
|
u.state = statePost
|
||||||
@ -258,14 +260,13 @@ func (u *RefUpdater) Post(ctx context.Context, alternateDirs ...string) error {
|
|||||||
statePost, u.state)
|
statePost, u.state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(u.refs) == 0 {
|
||||||
|
u.state = stateDone
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
out, err := u.hookClient.PostReceive(ctx, PostReceiveInput{
|
out, err := u.hookClient.PostReceive(ctx, PostReceiveInput{
|
||||||
RefUpdates: []ReferenceUpdate{
|
RefUpdates: u.refs,
|
||||||
{
|
|
||||||
Ref: u.ref,
|
|
||||||
Old: u.oldValue,
|
|
||||||
New: u.newValue,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Environment: Environment{
|
Environment: Environment{
|
||||||
AlternateObjectDirs: alternateDirs,
|
AlternateObjectDirs: alternateDirs,
|
||||||
},
|
},
|
||||||
@ -282,16 +283,16 @@ func (u *RefUpdater) Post(ctx context.Context, alternateDirs ...string) error {
|
|||||||
return nil
|
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",
|
cmd := command.New("show-ref",
|
||||||
command.WithFlag("--verify"),
|
command.WithFlag("--verify"),
|
||||||
command.WithFlag("-s"),
|
command.WithFlag("-s"),
|
||||||
command.WithArg(u.ref),
|
command.WithArg(ref),
|
||||||
)
|
)
|
||||||
output := &bytes.Buffer{}
|
output := &bytes.Buffer{}
|
||||||
err := cmd.Run(ctx, command.WithDir(u.repoPath), command.WithStdout(output))
|
err := cmd.Run(ctx, command.WithDir(u.repoPath), command.WithStdout(output))
|
||||||
if cErr := command.AsError(err); cErr != nil && cErr.IsExitCode(128) && cErr.IsInvalidRefErr() {
|
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 {
|
if err != nil {
|
||||||
|
118
git/merge.go
118
git/merge.go
@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/harness/gitness/git/merge"
|
"github.com/harness/gitness/git/merge"
|
||||||
"github.com/harness/gitness/git/parser"
|
"github.com/harness/gitness/git/parser"
|
||||||
"github.com/harness/gitness/git/sha"
|
"github.com/harness/gitness/git/sha"
|
||||||
|
"github.com/harness/gitness/git/sharedrepo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MergeParams is input structure object for merging operation.
|
// MergeParams is input structure object for merging operation.
|
||||||
@ -56,8 +57,7 @@ type MergeParams struct {
|
|||||||
// (optional, default: committer date)
|
// (optional, default: committer date)
|
||||||
AuthorDate *time.Time
|
AuthorDate *time.Time
|
||||||
|
|
||||||
RefType enum.RefType
|
Refs []RefUpdate
|
||||||
RefName string
|
|
||||||
|
|
||||||
// HeadExpectedSHA is commit sha on the head branch, if HeadExpectedSHA is older
|
// HeadExpectedSHA is commit sha on the head branch, if HeadExpectedSHA is older
|
||||||
// than the HeadBranch latest sha then merge will fail.
|
// than the HeadBranch latest sha then merge will fail.
|
||||||
@ -69,6 +69,19 @@ type MergeParams struct {
|
|||||||
Method enum.MergeMethod
|
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 {
|
func (p *MergeParams) Validate() error {
|
||||||
if err := p.WriteParams.Validate(); err != nil {
|
if err := p.WriteParams.Validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -82,9 +95,12 @@ func (p *MergeParams) Validate() error {
|
|||||||
return errors.InvalidArgument("head branch is mandatory")
|
return errors.InvalidArgument("head branch is mandatory")
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.RefType != enum.RefTypeUndefined && p.RefName == "" {
|
for _, ref := range p.Refs {
|
||||||
return errors.InvalidArgument("ref name has to be provided if type is defined")
|
if ref.Name == "" {
|
||||||
|
return errors.InvalidArgument("ref name has to be provided")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,27 +169,6 @@ func (s *Service) Merge(ctx context.Context, params *MergeParams) (MergeOutput,
|
|||||||
panic("unsupported merge method")
|
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
|
// find the commit SHAs
|
||||||
|
|
||||||
baseCommitSHA := params.BaseSHA
|
baseCommitSHA := params.BaseSHA
|
||||||
@ -259,35 +254,66 @@ func (s *Service) Merge(ctx context.Context, params *MergeParams) (MergeOutput,
|
|||||||
message = parser.CleanUpWhitespace(params.Message)
|
message = parser.CleanUpWhitespace(params.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge
|
// create merge commit and update the references
|
||||||
|
|
||||||
var refUpdater *hook.RefUpdater
|
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath)
|
||||||
|
if err != nil {
|
||||||
if params.RefType != enum.RefTypeUndefined {
|
return MergeOutput{}, fmt.Errorf("failed to create reference updater: %w", err)
|
||||||
refUpdater, err = hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath, refPath)
|
|
||||||
if err != nil {
|
|
||||||
return MergeOutput{}, errors.Internal(err, "failed to create ref updater object")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := refUpdater.InitOld(ctx, refOldValue); err != nil {
|
|
||||||
return MergeOutput{}, errors.Internal(err, "failed to set old reference value for ref updater")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mergeCommitSHA, conflicts, err := mergeFunc(
|
var mergeCommitSHA sha.SHA
|
||||||
ctx,
|
var conflicts []string
|
||||||
refUpdater,
|
|
||||||
repoPath, s.tmpDir,
|
err = sharedrepo.Run(ctx, refUpdater, s.tmpDir, repoPath, func(s *sharedrepo.SharedRepo) error {
|
||||||
&author, &committer,
|
mergeCommitSHA, conflicts, err = mergeFunc(
|
||||||
message,
|
ctx,
|
||||||
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) {
|
if errors.IsConflict(err) {
|
||||||
return MergeOutput{}, fmt.Errorf("failed to merge %q to %q in %q using the %q merge method: %w",
|
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)
|
params.HeadBranch, params.BaseBranch, params.RepoUID, mergeMethod, err)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return MergeOutput{}, errors.Internal(err, "failed to merge %q to %q in %q using the %q merge method",
|
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)
|
params.HeadBranch, params.BaseBranch, params.RepoUID, mergeMethod, err)
|
||||||
}
|
}
|
||||||
if len(conflicts) > 0 {
|
if len(conflicts) > 0 {
|
||||||
return MergeOutput{
|
return MergeOutput{
|
||||||
|
@ -20,222 +20,175 @@ import (
|
|||||||
|
|
||||||
"github.com/harness/gitness/errors"
|
"github.com/harness/gitness/errors"
|
||||||
"github.com/harness/gitness/git/api"
|
"github.com/harness/gitness/git/api"
|
||||||
"github.com/harness/gitness/git/hook"
|
|
||||||
"github.com/harness/gitness/git/sha"
|
"github.com/harness/gitness/git/sha"
|
||||||
"github.com/harness/gitness/git/sharedrepo"
|
"github.com/harness/gitness/git/sharedrepo"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type Params struct {
|
||||||
// errConflict is used to error out of sharedrepo Run method without erroring out of merge in case of conflicts.
|
Author, Committer *api.Signature
|
||||||
errConflict = errors.New("conflict")
|
Message string
|
||||||
)
|
MergeBaseSHA, TargetSHA, SourceSHA sha.SHA
|
||||||
|
}
|
||||||
|
|
||||||
// Func represents a merge method function. The concrete merge implementation functions must have this signature.
|
// Func represents a merge method function. The concrete merge implementation functions must have this signature.
|
||||||
type Func func(
|
type Func func(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
refUpdater *hook.RefUpdater,
|
s *sharedrepo.SharedRepo,
|
||||||
repoPath, tmpDir string,
|
params Params,
|
||||||
author, committer *api.Signature,
|
|
||||||
message string,
|
|
||||||
mergeBaseSHA, targetSHA, sourceSHA sha.SHA,
|
|
||||||
) (mergeSHA sha.SHA, conflicts []string, err error)
|
) (mergeSHA sha.SHA, conflicts []string, err error)
|
||||||
|
|
||||||
// Merge merges two the commits (targetSHA and sourceSHA) using the Merge method.
|
// Merge merges two the commits (targetSHA and sourceSHA) using the Merge method.
|
||||||
func Merge(
|
func Merge(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
refUpdater *hook.RefUpdater,
|
s *sharedrepo.SharedRepo,
|
||||||
repoPath, tmpDir string,
|
params Params,
|
||||||
author, committer *api.Signature,
|
|
||||||
message string,
|
|
||||||
mergeBaseSHA, targetSHA, sourceSHA sha.SHA,
|
|
||||||
) (mergeSHA sha.SHA, conflicts []string, err error) {
|
) (mergeSHA sha.SHA, conflicts []string, err error) {
|
||||||
return mergeInternal(ctx,
|
return mergeInternal(ctx, s, params, false)
|
||||||
refUpdater,
|
|
||||||
repoPath, tmpDir,
|
|
||||||
author, committer,
|
|
||||||
message,
|
|
||||||
mergeBaseSHA, targetSHA, sourceSHA,
|
|
||||||
false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Squash merges two the commits (targetSHA and sourceSHA) using the Squash method.
|
// Squash merges two the commits (targetSHA and sourceSHA) using the Squash method.
|
||||||
func Squash(
|
func Squash(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
refUpdater *hook.RefUpdater,
|
s *sharedrepo.SharedRepo,
|
||||||
repoPath, tmpDir string,
|
params Params,
|
||||||
author, committer *api.Signature,
|
|
||||||
message string,
|
|
||||||
mergeBaseSHA, targetSHA, sourceSHA sha.SHA,
|
|
||||||
) (mergeSHA sha.SHA, conflicts []string, err error) {
|
) (mergeSHA sha.SHA, conflicts []string, err error) {
|
||||||
return mergeInternal(ctx,
|
return mergeInternal(ctx, s, params, true)
|
||||||
refUpdater,
|
|
||||||
repoPath, tmpDir,
|
|
||||||
author, committer,
|
|
||||||
message,
|
|
||||||
mergeBaseSHA, targetSHA, sourceSHA,
|
|
||||||
true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeInternal is internal implementation of merge used for Merge and Squash methods.
|
// mergeInternal is internal implementation of merge used for Merge and Squash methods.
|
||||||
func mergeInternal(
|
func mergeInternal(ctx context.Context,
|
||||||
ctx context.Context,
|
s *sharedrepo.SharedRepo,
|
||||||
refUpdater *hook.RefUpdater,
|
params Params,
|
||||||
repoPath, tmpDir string,
|
|
||||||
author, committer *api.Signature,
|
|
||||||
message string,
|
|
||||||
mergeBaseSHA, targetSHA, sourceSHA sha.SHA,
|
|
||||||
squash bool,
|
squash bool,
|
||||||
) (mergeSHA sha.SHA, conflicts []string, err error) {
|
) (mergeSHA sha.SHA, conflicts []string, err error) {
|
||||||
err = sharedrepo.Run(ctx, refUpdater, tmpDir, repoPath, func(s *sharedrepo.SharedRepo) error {
|
mergeBaseSHA := params.MergeBaseSHA
|
||||||
var err error
|
targetSHA := params.TargetSHA
|
||||||
|
sourceSHA := params.SourceSHA
|
||||||
|
|
||||||
var treeSHA sha.SHA
|
var treeSHA sha.SHA
|
||||||
|
|
||||||
treeSHA, conflicts, err = s.MergeTree(ctx, mergeBaseSHA, targetSHA, sourceSHA)
|
treeSHA, conflicts, err = s.MergeTree(ctx, mergeBaseSHA, targetSHA, sourceSHA)
|
||||||
if err != nil {
|
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 {
|
if len(conflicts) > 0 {
|
||||||
return errConflict
|
return sha.None, conflicts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
parents := make([]sha.SHA, 0, 2)
|
parents := make([]sha.SHA, 0, 2)
|
||||||
parents = append(parents, targetSHA)
|
parents = append(parents, targetSHA)
|
||||||
if !squash {
|
if !squash {
|
||||||
parents = append(parents, sourceSHA)
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("commit tree failed: %w", err)
|
return sha.None, nil, 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 mergeSHA, conflicts, nil
|
return mergeSHA, conflicts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rebase merges two the commits (targetSHA and sourceSHA) using the Rebase method.
|
// 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.
|
//nolint:gocognit // refactor if needed.
|
||||||
func Rebase(
|
func Rebase(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
refUpdater *hook.RefUpdater,
|
s *sharedrepo.SharedRepo,
|
||||||
repoPath, tmpDir string,
|
params Params,
|
||||||
_, 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,
|
|
||||||
) (mergeSHA sha.SHA, conflicts []string, err error) {
|
) (mergeSHA sha.SHA, conflicts []string, err error) {
|
||||||
err = sharedrepo.Run(ctx, refUpdater, tmpDir, repoPath, func(s *sharedrepo.SharedRepo) error {
|
mergeBaseSHA := params.MergeBaseSHA
|
||||||
sourceSHAs, err := s.CommitSHAsForRebase(ctx, mergeBaseSHA, sourceSHA)
|
targetSHA := params.TargetSHA
|
||||||
if err != nil {
|
sourceSHA := params.SourceSHA
|
||||||
return fmt.Errorf("failed to find commit list in rebase merge: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
lastCommitSHA := targetSHA
|
sourceSHAs, err := s.CommitSHAsForRebase(ctx, mergeBaseSHA, sourceSHA)
|
||||||
lastTreeSHA, err := s.GetTreeSHA(ctx, targetSHA.String())
|
if err != nil {
|
||||||
if err != nil {
|
return sha.None, nil, fmt.Errorf("failed to find commit list in rebase merge: %w", err)
|
||||||
return fmt.Errorf("failed to get tree sha for target: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, commitSHA := range sourceSHAs {
|
|
||||||
var treeSHA sha.SHA
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// rebase merge preserves the commit author (and date) and the commit message, but changes the committer.
|
|
||||||
author := &commitInfo.Author
|
|
||||||
message := commitInfo.Title
|
|
||||||
if commitInfo.Message != "" {
|
|
||||||
message += "\n\n" + commitInfo.Message
|
|
||||||
}
|
|
||||||
|
|
||||||
var mergeTreeMergeBaseSHA sha.SHA
|
|
||||||
if len(commitInfo.ParentSHAs) > 0 {
|
|
||||||
// use parent of commit as merge base to only apply changes introduced by commit.
|
|
||||||
// See example usage of when --merge-base was introduced:
|
|
||||||
// https://github.com/git/git/commit/66265a693e8deb3ab86577eb7f69940410044081
|
|
||||||
//
|
|
||||||
// NOTE: CommitSHAsForRebase only returns non-merge commits.
|
|
||||||
mergeTreeMergeBaseSHA = commitInfo.ParentSHAs[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
treeSHA, conflicts, err = s.MergeTree(ctx, mergeTreeMergeBaseSHA, lastCommitSHA, commitSHA)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to merge tree in rebase merge: %w", err)
|
|
||||||
}
|
|
||||||
if len(conflicts) > 0 {
|
|
||||||
return errConflict
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drop any commit which after being rebased would be empty.
|
|
||||||
// There's two cases in which that can happen:
|
|
||||||
// 1. Empty commit.
|
|
||||||
// Github is dropping empty commits, so we'll do the same.
|
|
||||||
// 2. The changes of the commit already exist on the target branch.
|
|
||||||
// Git's `git rebase` is dropping such commits on default (and so does Github)
|
|
||||||
// https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---emptydropkeepask
|
|
||||||
if treeSHA.Equal(lastTreeSHA) {
|
|
||||||
log.Ctx(ctx).Debug().Msgf("skipping commit %s as it's empty after rebase", commitSHA)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
lastCommitSHA, err = s.CommitTree(ctx, author, committer, treeSHA, message, false, lastCommitSHA)
|
|
||||||
if err != nil {
|
|
||||||
return 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
|
lastCommitSHA := targetSHA
|
||||||
|
lastTreeSHA, err := s.GetTreeSHA(ctx, targetSHA.String())
|
||||||
|
if err != nil {
|
||||||
|
return sha.None, nil, fmt.Errorf("failed to get tree sha for target: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, commitSHA := range sourceSHAs {
|
||||||
|
var treeSHA sha.SHA
|
||||||
|
|
||||||
|
commitInfo, err := api.GetCommit(ctx, s.Directory(), commitSHA.String())
|
||||||
|
if err != nil {
|
||||||
|
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.
|
||||||
|
author := &commitInfo.Author
|
||||||
|
message := commitInfo.Title
|
||||||
|
if commitInfo.Message != "" {
|
||||||
|
message += "\n\n" + commitInfo.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
var mergeTreeMergeBaseSHA sha.SHA
|
||||||
|
if len(commitInfo.ParentSHAs) > 0 {
|
||||||
|
// use parent of commit as merge base to only apply changes introduced by commit.
|
||||||
|
// See example usage of when --merge-base was introduced:
|
||||||
|
// https://github.com/git/git/commit/66265a693e8deb3ab86577eb7f69940410044081
|
||||||
|
//
|
||||||
|
// NOTE: CommitSHAsForRebase only returns non-merge commits.
|
||||||
|
mergeTreeMergeBaseSHA = commitInfo.ParentSHAs[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
treeSHA, conflicts, err = s.MergeTree(ctx, mergeTreeMergeBaseSHA, lastCommitSHA, commitSHA)
|
||||||
|
if err != nil {
|
||||||
|
return sha.None, nil, fmt.Errorf("failed to merge tree in rebase merge: %w", err)
|
||||||
|
}
|
||||||
|
if len(conflicts) > 0 {
|
||||||
|
return sha.None, conflicts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop any commit which after being rebased would be empty.
|
||||||
|
// There's two cases in which that can happen:
|
||||||
|
// 1. Empty commit.
|
||||||
|
// Github is dropping empty commits, so we'll do the same.
|
||||||
|
// 2. The changes of the commit already exist on the target branch.
|
||||||
|
// Git's `git rebase` is dropping such commits on default (and so does Github)
|
||||||
|
// https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---emptydropkeepask
|
||||||
|
if treeSHA.Equal(lastTreeSHA) {
|
||||||
|
log.Ctx(ctx).Debug().Msgf("skipping commit %s as it's empty after rebase", commitSHA)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lastCommitSHA, err = s.CommitTree(ctx, author, params.Committer, treeSHA, message, false, lastCommitSHA)
|
||||||
|
if err != nil {
|
||||||
|
return sha.None, nil, fmt.Errorf("failed to commit tree in rebase merge: %w", err)
|
||||||
|
}
|
||||||
|
lastTreeSHA = treeSHA
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeSHA = lastCommitSHA
|
||||||
|
|
||||||
|
return mergeSHA, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FastForward points the is internal implementation of merge used for Merge and Squash methods.
|
// 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(
|
func FastForward(
|
||||||
ctx context.Context,
|
_ context.Context,
|
||||||
refUpdater *hook.RefUpdater,
|
_ *sharedrepo.SharedRepo,
|
||||||
repoPath, tmpDir string,
|
params Params,
|
||||||
_, _ *api.Signature, // commit author and committer aren't used here
|
|
||||||
_ string, // commit message isn't used here
|
|
||||||
mergeBaseSHA, targetSHA, sourceSHA sha.SHA,
|
|
||||||
) (mergeSHA sha.SHA, conflicts []string, err error) {
|
) (mergeSHA sha.SHA, conflicts []string, err error) {
|
||||||
|
mergeBaseSHA := params.MergeBaseSHA
|
||||||
|
targetSHA := params.TargetSHA
|
||||||
|
sourceSHA := params.SourceSHA
|
||||||
|
|
||||||
if targetSHA != mergeBaseSHA {
|
if targetSHA != mergeBaseSHA {
|
||||||
return sha.None, nil,
|
return sha.None, nil,
|
||||||
errors.Conflict("Target branch has diverged from the source branch. Fast-forward not possible.")
|
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
|
return sourceSHA, nil, nil
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ func (s *Service) CommitFiles(ctx context.Context, params *CommitFilesParams) (C
|
|||||||
refOldSHA = commit.SHA
|
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 {
|
if err != nil {
|
||||||
return CommitFilesResponse{}, fmt.Errorf("failed to create ref updater: %w", err)
|
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
|
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)
|
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)
|
reference, err := GetRefPath(params.Name, params.Type)
|
||||||
if err != nil {
|
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 {
|
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)
|
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
|
return refPullReqPrefix + refName + refPullReqHeadSuffix, nil
|
||||||
case enum.RefTypePullReqMerge:
|
case enum.RefTypePullReqMerge:
|
||||||
return refPullReqPrefix + refName + refPullReqMergeSuffix, nil
|
return refPullReqPrefix + refName + refPullReqMergeSuffix, nil
|
||||||
case enum.RefTypeUndefined:
|
|
||||||
fallthrough
|
|
||||||
default:
|
default:
|
||||||
return "", errors.InvalidArgument("provided reference type '%s' is invalid", refType)
|
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
|
// ref updater
|
||||||
|
|
||||||
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath, tagRef)
|
refUpdater, err := hook.CreateRefUpdater(s.hookClientFactory, params.EnvVars, repoPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create ref updater to create the tag: %w", err)
|
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)
|
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)
|
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)
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create ref updater to delete the tag: %w", err)
|
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) {
|
if errors.IsNotFound(err) {
|
||||||
return errors.NotFound("tag %q does not exist", params.Name)
|
return errors.NotFound("tag %q does not exist", params.Name)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user