Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'internal/gitaly/service/operations/merge.go')
-rw-r--r--internal/gitaly/service/operations/merge.go112
1 files changed, 108 insertions, 4 deletions
diff --git a/internal/gitaly/service/operations/merge.go b/internal/gitaly/service/operations/merge.go
index 6eef9c304..5047b16b3 100644
--- a/internal/gitaly/service/operations/merge.go
+++ b/internal/gitaly/service/operations/merge.go
@@ -1,6 +1,7 @@
package operations
import (
+ "bytes"
"context"
"errors"
"fmt"
@@ -9,12 +10,14 @@ import (
"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus"
"github.com/sirupsen/logrus"
+ "gitlab.com/gitlab-org/gitaly/v15/internal/command"
"gitlab.com/gitlab-org/gitaly/v15/internal/git"
"gitlab.com/gitlab-org/gitaly/v15/internal/git/localrepo"
"gitlab.com/gitlab-org/gitaly/v15/internal/git/updateref"
"gitlab.com/gitlab-org/gitaly/v15/internal/git2go"
"gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/hook"
"gitlab.com/gitlab-org/gitaly/v15/internal/helper"
+ "gitlab.com/gitlab-org/gitaly/v15/internal/helper/text"
"gitlab.com/gitlab-org/gitaly/v15/internal/metadata/featureflag"
"gitlab.com/gitlab-org/gitaly/v15/proto/go/gitalypb"
)
@@ -47,6 +50,58 @@ func validateMergeBranchRequest(request *gitalypb.UserMergeBranchRequest) error
return nil
}
+func createCommitFromTree(
+ ctx context.Context,
+ quarantineRepo *localrepo.Repo,
+ resultTree []byte,
+ authorName string,
+ authorMail string,
+ authorDate time.Time,
+ message string,
+ ours string,
+ theirs string,
+) (string, error) {
+ commitEnv := []string{
+ "GIT_COMMITTER_NAME=" + authorName,
+ "GIT_COMMITTER_EMAIL=" + authorMail,
+ "GIT_COMMITTER_DATE=" + authorDate.String(),
+ "GIT_AUTHOR_NAME=" + authorName,
+ "GIT_AUTHOR_EMAIL=" + authorMail,
+ "GIT_AUTHOR_DATE=" + authorDate.String(),
+ }
+
+ flags := []git.Option{
+ git.ValueFlag{
+ Name: "-m",
+ Value: message,
+ },
+ git.ValueFlag{
+ Name: "-p",
+ Value: ours,
+ },
+ git.ValueFlag{
+ Name: "-p",
+ Value: theirs,
+ },
+ }
+
+ var stdout, stderr bytes.Buffer
+ if err := quarantineRepo.ExecAndWait(ctx, git.SubCmd{
+ Name: "commit-tree",
+ Flags: flags,
+ Args: []string{
+ string(resultTree),
+ },
+ }, git.WithStdout(&stdout), git.WithStderr(&stderr), git.WithEnv(commitEnv...)); err != nil {
+ return "", fmt.Errorf("creating commit: %w", gitError{
+ Err: err,
+ ErrMsg: stderr.String(),
+ })
+ }
+
+ return text.ChompBytes(stdout.Bytes()), nil
+}
+
func (s *Server) merge(
ctx context.Context,
repoPath string,
@@ -58,7 +113,44 @@ func (s *Server) merge(
ours string,
theirs string,
) (string, error) {
- return "", helper.ErrInternalf("UserMergeBranch using Git is not yet implemented")
+ var mergeTreeStderr bytes.Buffer
+ var mergeTreeStdout bytes.Buffer
+ cmdMergeTree, err := s.gitCmdFactory.New(ctx, quarantineRepo,
+ git.SubCmd{
+ Name: "merge-tree",
+ Flags: []git.Option{
+ git.Flag{Name: "--write-tree"},
+ git.Flag{Name: "--name-only"},
+ },
+ Args: []string{ours, theirs},
+ },
+ git.WithStderr(&mergeTreeStderr),
+ git.WithStdout(&mergeTreeStdout),
+ )
+ if err != nil {
+ return "", fmt.Errorf("merge-tree: %w", gitError{ErrMsg: mergeTreeStderr.String(), Err: err})
+ }
+
+ cmdErr := cmdMergeTree.Wait()
+ if exitCode, success := command.ExitStatus(cmdErr); success && exitCode > 1 {
+ return "", fmt.Errorf("merge-tree: %w", gitError{ErrMsg: mergeTreeStderr.String(), Err: err})
+ }
+
+ mergeTreeResult, err := git.NewMergeTreeResult(mergeTreeStdout.String())
+ if err != nil {
+ return "", err
+ }
+
+ var commit string
+ if commit, err = createCommitFromTree(
+ ctx, quarantineRepo, []byte(mergeTreeResult.TreeOid),
+ authorName, authorMail, authorDate,
+ message, ours, theirs,
+ ); err != nil {
+ return "", fmt.Errorf("create commit from tree: %w", err)
+ }
+
+ return commit, nil
}
func (s *Server) mergeWithGit2Go(
@@ -82,6 +174,13 @@ func (s *Server) mergeWithGit2Go(
Theirs: theirs,
})
if err != nil {
+ var conflictErr git2go.ConflictingFilesError
+ if errors.As(err, &conflictErr) {
+ return "", &git.ConflictingFilesError{
+ ConflictingFiles: conflictErr.ConflictingFiles,
+ }
+ }
+
if errors.Is(err, git2go.ErrInvalidArgument) {
return "", helper.ErrInvalidArgument(err)
}
@@ -130,7 +229,12 @@ func (s *Server) UserMergeBranch(stream gitalypb.OperationService_UserMergeBranc
var mergeCommitID string
var mergeErr error
- if featureflag.MergeTreeMerge.IsEnabled(ctx) {
+ version, err := s.gitCmdFactory.GitVersion(ctx)
+ if err != nil {
+ return helper.ErrInternalf("getting git version: %w", err)
+ }
+
+ if featureflag.MergeTreeMerge.IsEnabled(ctx) && version.IsMergeTreeSupported() {
mergeCommitID, mergeErr = s.merge(ctx, repoPath, quarantineRepo,
string(firstRequest.User.Name),
string(firstRequest.User.Email),
@@ -149,7 +253,7 @@ func (s *Server) UserMergeBranch(stream gitalypb.OperationService_UserMergeBranc
}
if mergeErr != nil {
- var conflictErr git2go.ConflictingFilesError
+ var conflictErr *git.ConflictingFilesError
if errors.As(mergeErr, &conflictErr) {
conflictingFiles := make([][]byte, 0, len(conflictErr.ConflictingFiles))
for _, conflictingFile := range conflictErr.ConflictingFiles {
@@ -157,7 +261,7 @@ func (s *Server) UserMergeBranch(stream gitalypb.OperationService_UserMergeBranc
}
detailedErr, err := helper.ErrWithDetails(
- helper.ErrFailedPreconditionf("merging commits: %w", err),
+ helper.ErrFailedPreconditionf("merging commits: %w", mergeErr),
&gitalypb.UserMergeBranchError{
Error: &gitalypb.UserMergeBranchError_MergeConflict{
MergeConflict: &gitalypb.MergeConflictError{