From 33be763216ca77abe20a5dd4beb865d0fcad433c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Ga=C4=87e=C5=A1a?= Date: Thu, 26 Feb 2026 14:56:58 +0100 Subject: [PATCH 1/2] Change fork-sync API status code for conflicts --- app/api/controller/repo/fork_sync.go | 47 +++++++++++++--------------- app/api/handler/repo/fork_sync.go | 9 ++++-- app/api/openapi/repo.go | 1 + types/fork.go | 11 ++++--- 4 files changed, 37 insertions(+), 31 deletions(-) diff --git a/app/api/controller/repo/fork_sync.go b/app/api/controller/repo/fork_sync.go index c8f05a1233..7587d223be 100644 --- a/app/api/controller/repo/fork_sync.go +++ b/app/api/controller/repo/fork_sync.go @@ -49,6 +49,10 @@ func (in *ForkSyncInput) sanitize() error { return errors.InvalidArgument("Branch commit SHA must be provided") } + if in.BranchUpstream == "" { + in.BranchUpstream = in.Branch + } + return nil } @@ -58,19 +62,14 @@ func (c *Controller) ForkSync( session *auth.Session, repoRef string, in *ForkSyncInput, -) (*types.ForkSyncOutput, error) { +) (*types.ForkSyncOutput, *types.ForkSyncConflict, error) { repoForkCore, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoPush) if err != nil { - return nil, err + return nil, nil, err } if err := in.sanitize(); err != nil { - return nil, err - } - - branchUpstreamName := in.BranchUpstream - if branchUpstreamName == "" { - branchUpstreamName = in.Branch + return nil, nil, err } branchForkInfo, err := c.git.GetRef(ctx, git.GetRefParams{ @@ -79,11 +78,11 @@ func (c *Controller) ForkSync( Type: gitenum.RefTypeBranch, }) if err != nil { - return nil, fmt.Errorf("failed to get repo branch: %w", err) + return nil, nil, fmt.Errorf("failed to get repo branch: %w", err) } if !branchForkInfo.SHA.Equal(in.BranchCommitSHA) { - return nil, errors.InvalidArgumentf("The commit %s isn't the latest commit on the branch %s", + return nil, nil, errors.InvalidArgumentf("The commit %s isn't the latest commit on the branch %s", in.BranchCommitSHA, in.Branch) } @@ -91,10 +90,10 @@ func (c *Controller) ForkSync( ctx, session, repoForkCore, - branchUpstreamName, + in.BranchUpstream, ) if err != nil { - return nil, fmt.Errorf("failed to fetch upstream branch: %w", err) + return nil, nil, fmt.Errorf("failed to fetch upstream branch: %w", err) } ancestorResult, err := c.git.IsAncestor(ctx, git.IsAncestorParams{ @@ -103,14 +102,14 @@ func (c *Controller) ForkSync( DescendantCommitSHA: branchForkInfo.SHA, }) if err != nil { - return nil, fmt.Errorf("failed to check if the upstream commit is ancestor: %w", err) + return nil, nil, fmt.Errorf("failed to check if the upstream commit is ancestor: %w", err) } if ancestorResult.Ancestor { // The branch already contains the latest commit from the upstream repository branch - nothing to do. return &types.ForkSyncOutput{ AlreadyAncestor: true, - }, nil + }, nil, nil } mergeBase, err := c.git.MergeBase(ctx, git.MergeBaseParams{ @@ -119,7 +118,7 @@ func (c *Controller) ForkSync( Ref2: branchForkInfo.SHA.String(), }) if err != nil { - return nil, fmt.Errorf("failed to find merge base: %w", err) + return nil, nil, fmt.Errorf("failed to find merge base: %w", err) } var ( @@ -133,7 +132,7 @@ func (c *Controller) ForkSync( mergeMethod = gitenum.MergeMethodMerge message = fmt.Sprintf("Merge upstream branch '%s' of %s", - branchUpstreamName, repoUpstreamCore.Path) + in.BranchUpstream, repoUpstreamCore.Path) committer = controller.SystemServicePrincipalInfo() author = controller.IdentityFromPrincipalInfo(*session.Principal.ToPrincipalInfo()) } @@ -142,7 +141,7 @@ func (c *Controller) ForkSync( headBranchRef, err := git.GetRefPath(in.Branch, gitenum.RefTypeBranch) if err != nil { - return nil, fmt.Errorf("failed to generate ref name: %w", err) + return nil, nil, fmt.Errorf("failed to generate ref name: %w", err) } refs = append(refs, git.RefUpdate{ @@ -155,12 +154,11 @@ func (c *Controller) ForkSync( writeParams, err := controller.CreateRPCSystemReferencesWriteParams(ctx, c.urlProvider, session, repoForkCore) if err != nil { - return nil, fmt.Errorf("failed to create RPC write params: %w", err) + return nil, nil, fmt.Errorf("failed to create RPC write params: %w", err) } mergeOutput, err := c.git.Merge(ctx, &git.MergeParams{ - WriteParams: writeParams, - //HeadRepoUID: repoUpstreamCore.GitUID, // TODO: Remove HeadRepoUID! + WriteParams: writeParams, BaseSHA: branchForkInfo.SHA, HeadSHA: branchUpstreamSHA, Message: message, @@ -172,18 +170,17 @@ func (c *Controller) ForkSync( Method: mergeMethod, }) if err != nil { - return nil, fmt.Errorf("fork branch sync merge failed: %w", err) + return nil, nil, fmt.Errorf("fork branch sync merge failed: %w", err) } if mergeOutput.MergeSHA.IsEmpty() || len(mergeOutput.ConflictFiles) > 0 { - return &types.ForkSyncOutput{ + return nil, &types.ForkSyncConflict{ ConflictFiles: mergeOutput.ConflictFiles, - Message: fmt.Sprintf("Branch synchronization blocked by conflicting files: %v", - mergeOutput.ConflictFiles), + Message: "Branch synchronization blocked by conflicting files.", }, nil } return &types.ForkSyncOutput{ NewCommitSHA: mergeOutput.MergeSHA, - }, nil + }, nil, nil } diff --git a/app/api/handler/repo/fork_sync.go b/app/api/handler/repo/fork_sync.go index ede015fb4d..9ed280199e 100644 --- a/app/api/handler/repo/fork_sync.go +++ b/app/api/handler/repo/fork_sync.go @@ -41,12 +41,17 @@ func HandleForkSync(repoCtrl *repo.Controller) http.HandlerFunc { return } - result, err := repoCtrl.ForkSync(ctx, session, repoRef, in) + resultMerge, resultConflict, err := repoCtrl.ForkSync(ctx, session, repoRef, in) if err != nil { render.TranslatedUserError(ctx, w, err) return } - render.JSON(w, http.StatusOK, result) + if resultConflict != nil { + render.JSON(w, http.StatusConflict, resultConflict) + return + } + + render.JSON(w, http.StatusOK, resultMerge) } } diff --git a/app/api/openapi/repo.go b/app/api/openapi/repo.go index 002f049cd3..5b942fc5c7 100644 --- a/app/api/openapi/repo.go +++ b/app/api/openapi/repo.go @@ -1532,6 +1532,7 @@ func repoOperations(reflector *openapi3.Reflector) { repo.ForkSyncInput }{}, http.MethodPost) _ = reflector.SetJSONResponse(&opForkSyncBranch, new(types.ForkSyncOutput), http.StatusOK) + _ = reflector.SetJSONResponse(&opForkSyncBranch, new(types.ForkSyncConflict), http.StatusConflict) _ = reflector.SetJSONResponse(&opForkSyncBranch, new(usererror.Error), http.StatusBadRequest) _ = reflector.SetJSONResponse(&opForkSyncBranch, new(usererror.Error), http.StatusInternalServerError) _ = reflector.SetJSONResponse(&opForkSyncBranch, new(usererror.Error), http.StatusUnauthorized) diff --git a/types/fork.go b/types/fork.go index 3e300f34e8..0430ec55c7 100644 --- a/types/fork.go +++ b/types/fork.go @@ -17,8 +17,11 @@ package types import "github.com/harness/gitness/git/sha" type ForkSyncOutput struct { - AlreadyAncestor bool `json:"already_ancestor,omitempty"` - NewCommitSHA sha.SHA `json:"new_commit_sha,omitzero"` - ConflictFiles []string `json:"conflict_files,omitempty"` - Message string `json:"message,omitempty"` + AlreadyAncestor bool `json:"already_ancestor,omitempty"` + NewCommitSHA sha.SHA `json:"new_commit_sha,omitzero"` +} + +type ForkSyncConflict struct { + ConflictFiles []string `json:"conflict_files,omitempty"` + Message string `json:"message,omitempty"` } From db1473559e4de328da6c06a8dc7adccefff39060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Ga=C4=87e=C5=A1a?= Date: Thu, 26 Feb 2026 15:33:27 +0100 Subject: [PATCH 2/2] resolve code comments --- app/api/controller/repo/fork_sync.go | 4 ++-- types/fork.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/api/controller/repo/fork_sync.go b/app/api/controller/repo/fork_sync.go index 7587d223be..c13e4c18ca 100644 --- a/app/api/controller/repo/fork_sync.go +++ b/app/api/controller/repo/fork_sync.go @@ -141,7 +141,7 @@ func (c *Controller) ForkSync( headBranchRef, err := git.GetRefPath(in.Branch, gitenum.RefTypeBranch) if err != nil { - return nil, nil, fmt.Errorf("failed to generate ref name: %w", err) + return nil, nil, fmt.Errorf("failed to get ref path: %w", err) } refs = append(refs, git.RefUpdate{ @@ -154,7 +154,7 @@ func (c *Controller) ForkSync( writeParams, err := controller.CreateRPCSystemReferencesWriteParams(ctx, c.urlProvider, session, repoForkCore) if err != nil { - return nil, nil, fmt.Errorf("failed to create RPC write params: %w", err) + return nil, nil, fmt.Errorf("failed to create RPC sys ref write params: %w", err) } mergeOutput, err := c.git.Merge(ctx, &git.MergeParams{ diff --git a/types/fork.go b/types/fork.go index 0430ec55c7..c837274007 100644 --- a/types/fork.go +++ b/types/fork.go @@ -22,6 +22,6 @@ type ForkSyncOutput struct { } type ForkSyncConflict struct { - ConflictFiles []string `json:"conflict_files,omitempty"` - Message string `json:"message,omitempty"` + ConflictFiles []string `json:"conflict_files"` + Message string `json:"message"` }