Merge branch 'eb/code-225' of _OKE5H2PQKOUfzFFDuD4FA/default/CODE/gitness (#70)

This commit is contained in:
Enver Bisevac 2023-05-15 16:21:08 +00:00 committed by Harness
commit 1b04c8f98e
5 changed files with 102 additions and 14 deletions

View File

@ -18,6 +18,7 @@ import (
"github.com/harness/gitness/gitrpc/enum"
"github.com/harness/gitness/gitrpc/internal/tempdir"
"github.com/harness/gitness/gitrpc/internal/types"
"github.com/rs/zerolog/log"
"code.gitea.io/gitea/modules/git"
)
@ -175,7 +176,7 @@ func (g Adapter) CreateTemporaryRepoForPR(
func runMergeCommand(
ctx context.Context,
pr *types.PullRequest,
mergeMethod string,
mergeMethod enum.MergeMethod,
cmd *git.Command,
tmpBasePath string,
env []string,
@ -254,6 +255,7 @@ func (g Adapter) Merge(
ctx context.Context,
pr *types.PullRequest,
mergeMethod enum.MergeMethod,
baseBranch string,
trackingBranch string,
tmpBasePath string,
mergeMsg string,
@ -268,13 +270,14 @@ func (g Adapter) Merge(
mergeMsg = "Merge commit"
}
stagingBranch := "staging"
// TODO: sign merge commit
signArg := "--no-gpg-sign"
switch mergeMethod {
case enum.MergeMethodMerge:
cmd := git.NewCommand(ctx, "merge", "--no-ff", "--no-commit", trackingBranch)
if err := runMergeCommand(ctx, pr, string(mergeMethod), cmd, tmpBasePath, env); err != nil {
if err := runMergeCommand(ctx, pr, mergeMethod, cmd, tmpBasePath, env); err != nil {
return fmt.Errorf("unable to merge tracking into base: %w", err)
}
@ -284,7 +287,7 @@ func (g Adapter) Merge(
case enum.MergeMethodSquash:
// Merge with squash
cmd := git.NewCommand(ctx, "merge", "--squash", trackingBranch)
if err := runMergeCommand(ctx, pr, string(mergeMethod), cmd, tmpBasePath, env); err != nil {
if err := runMergeCommand(ctx, pr, mergeMethod, cmd, tmpBasePath, env); err != nil {
return fmt.Errorf("unable to merge --squash tracking into base: %v", err)
}
@ -311,6 +314,79 @@ func (g Adapter) Merge(
pr.HeadBranch, pr.BaseBranch, outbuf.String(), errbuf.String())
}
}
case enum.MergeMethodRebase:
// Checkout head branch
if err := git.NewCommand(ctx, "checkout", "-b", stagingBranch, trackingBranch).
Run(&git.RunOpts{
Dir: tmpBasePath,
Stdout: &outbuf,
Stderr: &errbuf,
}); err != nil {
return fmt.Errorf("git checkout base prior to merge post staging rebase [%s -> %s]: %v\n%s\n%s", pr.HeadBranch, pr.BaseBranch, err, outbuf.String(), errbuf.String())
}
outbuf.Reset()
errbuf.Reset()
// Rebase before merging
if err := git.NewCommand(ctx, "rebase", baseBranch).
Run(&git.RunOpts{
Dir: tmpBasePath,
Stdout: &outbuf,
Stderr: &errbuf,
}); err != nil {
// Rebase will leave a REBASE_HEAD file in .git if there is a conflict
if _, statErr := os.Stat(filepath.Join(tmpBasePath, ".git", "REBASE_HEAD")); statErr == nil {
var commitSha string
// TBD git version we will support
// failingCommitPath := filepath.Join(tmpBasePath, ".git", "rebase-apply", "original-commit") // Git < 2.26
// if _, statErr := os.Stat(failingCommitPath); statErr != nil {
// return fmt.Errorf("git rebase staging on to base [%s -> %s]: %v\n%s\n%s", pr.HeadBranch, pr.BaseBranch, err, outbuf.String(), errbuf.String())
// }
failingCommitPath := filepath.Join(tmpBasePath, ".git", "rebase-merge", "stopped-sha") // Git >= 2.26
if _, statErr := os.Stat(failingCommitPath); statErr != nil {
return fmt.Errorf("git rebase staging on to base [%s -> %s]: %v\n%s\n%s", pr.HeadBranch, pr.BaseBranch, err, outbuf.String(), errbuf.String())
}
commitShaBytes, readErr := os.ReadFile(failingCommitPath)
if readErr != nil {
// Abandon this attempt to handle the error
return fmt.Errorf("git rebase staging on to base [%s -> %s]: %v\n%s\n%s", pr.HeadBranch, pr.BaseBranch, err, outbuf.String(), errbuf.String())
}
commitSha = strings.TrimSpace(string(commitShaBytes))
log.Debug().Msgf("RebaseConflict at %s [%s -> %s]: %v\n%s\n%s", commitSha, pr.HeadBranch, pr.BaseBranch, err, outbuf.String(), errbuf.String())
return &types.MergeConflictsError{
Method: mergeMethod,
CommitSHA: commitSha,
StdOut: outbuf.String(),
StdErr: errbuf.String(),
Err: err,
}
}
return fmt.Errorf("git rebase staging on to base [%s -> %s]: %v\n%s\n%s", pr.HeadBranch, pr.BaseBranch, err, outbuf.String(), errbuf.String())
}
outbuf.Reset()
errbuf.Reset()
// Checkout base branch again
if err := git.NewCommand(ctx, "checkout", baseBranch).
Run(&git.RunOpts{
Dir: tmpBasePath,
Stdout: &outbuf,
Stderr: &errbuf,
}); err != nil {
return fmt.Errorf("git checkout base prior to merge post staging rebase [%s -> %s]: %v\n%s\n%s", pr.HeadBranch, pr.BaseBranch, err, outbuf.String(), errbuf.String())
}
outbuf.Reset()
errbuf.Reset()
cmd := git.NewCommand(ctx, "merge", "--ff-only", stagingBranch)
// Prepare merge with commit
if err := runMergeCommand(ctx, pr, mergeMethod, cmd, tmpBasePath, env); err != nil {
return err
}
default:
return fmt.Errorf("wrong merge method provided: %s", mergeMethod)
}

View File

@ -46,7 +46,7 @@ type GitAdapter interface {
GetRefPath(refName string, refType enum.RefType) (string, error)
UpdateRef(ctx context.Context, repoPath, refName string, refType enum.RefType, newValue, oldValue string) error
CreateTemporaryRepoForPR(ctx context.Context, reposTempPath string, pr *types.PullRequest) (string, error)
Merge(ctx context.Context, pr *types.PullRequest, mergeMethod enum.MergeMethod, trackingBranch string,
Merge(ctx context.Context, pr *types.PullRequest, mergeMethod enum.MergeMethod, baseBranch, trackingBranch string,
tmpBasePath string, mergeMsg string, env []string, identity *types.Identity) error
GetMergeBase(ctx context.Context, repoPath, remote, base, head string) (string, string, error)
GetDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string) (string, error)

View File

@ -169,10 +169,19 @@ func (s MergeService) Merge(
mergeMsg += "\n\n" + strings.TrimSpace(request.Message)
}
if err = s.adapter.Merge(ctx, pr, enum.MergeMethodFromRPC(request.Method), trackingBranch, tmpBasePath, mergeMsg, env, &types.Identity{
Name: author.Name,
Email: author.Email,
}); err != nil {
if err = s.adapter.Merge(
ctx,
pr,
enum.MergeMethodFromRPC(request.Method),
baseBranch,
trackingBranch,
tmpBasePath,
mergeMsg,
env,
&types.Identity{
Name: author.Name,
Email: author.Email,
}); err != nil {
return nil, processGitErrorf(err, "merge failed")
}

View File

@ -7,6 +7,8 @@ package types
import (
"errors"
"fmt"
"github.com/harness/gitness/gitrpc/enum"
)
var (
@ -29,10 +31,11 @@ var (
// MergeConflictsError represents an error if merging fails with a conflict.
type MergeConflictsError struct {
Method string
StdOut string
StdErr string
Err error
Method enum.MergeMethod
CommitSHA string
StdOut string
StdErr string
Err error
}
func IsMergeConflictsError(err error) bool {
@ -55,7 +58,7 @@ func (e *MergeConflictsError) Is(target error) bool {
// MergeUnrelatedHistoriesError represents an error if merging fails due to unrelated histories.
type MergeUnrelatedHistoriesError struct {
Method string
Method enum.MergeMethod
StdOut string
StdErr string
Err error

View File

@ -88,7 +88,7 @@ export const PullRequestActionsBox: React.FC<PullRequestActionsBoxProps> = ({
method: 'rebase',
title: getString('pr.mergeOptions.rebaseAndMerge'),
desc: getString('pr.mergeOptions.rebaseAndMergeDesc'),
disabled: true
disabled: false
},
{
method: 'close',