mirror of
https://github.com/harness/drone.git
synced 2025-05-01 05:00:00 +00:00
[feat] initial work on merging PR (#170)
* initial work on merging PR * code refactored based on requested changes * requested changes
This commit is contained in:
parent
1a84e19ce4
commit
63de576d08
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
.DS_Store
|
||||
NOTES*
|
||||
__debug_bin
|
||||
_research
|
||||
.env
|
||||
*.sqlite
|
||||
|
@ -7,6 +7,7 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/events"
|
||||
"github.com/harness/gitness/gitrpc"
|
||||
server2 "github.com/harness/gitness/gitrpc/server"
|
||||
@ -106,7 +107,7 @@ func initSystem(ctx context.Context, config *types.Config) (*system, error) {
|
||||
pullReqActivityStore := database.ProvidePullReqActivityStore(db)
|
||||
pullReqReviewStore := database.ProvidePullReqReviewStore(db)
|
||||
pullReqReviewerStore := database.ProvidePullReqReviewerStore(db)
|
||||
pullreqController := pullreq.ProvideController(db, authorizer, pullReqStore, pullReqActivityStore, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, gitrpcInterface)
|
||||
pullreqController := pullreq.ProvideController(db, provider, authorizer, pullReqStore, pullReqActivityStore, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, gitrpcInterface)
|
||||
webhookStore := database.ProvideWebhookStore(db)
|
||||
webhookExecutionStore := database.ProvideWebhookExecutionStore(db)
|
||||
webhookConfig := ProvideWebhookConfig(config)
|
||||
|
@ -7,6 +7,7 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/events"
|
||||
"github.com/harness/gitness/gitrpc"
|
||||
server2 "github.com/harness/gitness/gitrpc/server"
|
||||
@ -67,7 +68,7 @@ func initSystem(ctx context.Context, config *types.Config) (*system, error) {
|
||||
pullReqActivityStore := database.ProvidePullReqActivityStore(db)
|
||||
pullReqReviewStore := database.ProvidePullReqReviewStore(db)
|
||||
pullReqReviewerStore := database.ProvidePullReqReviewerStore(db)
|
||||
pullreqController := pullreq.ProvideController(db, authorizer, pullReqStore, pullReqActivityStore, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, gitrpcInterface)
|
||||
pullreqController := pullreq.ProvideController(db, provider, authorizer, pullReqStore, pullReqActivityStore, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, gitrpcInterface)
|
||||
webhookStore := database.ProvideWebhookStore(db)
|
||||
webhookExecutionStore := database.ProvideWebhookExecutionStore(db)
|
||||
webhookConfig := ProvideWebhookConfig(config)
|
||||
|
@ -18,6 +18,7 @@ type Client struct {
|
||||
httpService rpc.SmartHTTPServiceClient
|
||||
commitFilesService rpc.CommitFilesServiceClient
|
||||
diffService rpc.DiffServiceClient
|
||||
mergeService rpc.MergeServiceClient
|
||||
}
|
||||
|
||||
func New(remoteAddr string) (*Client, error) {
|
||||
@ -47,5 +48,6 @@ func New(remoteAddr string) (*Client, error) {
|
||||
httpService: rpc.NewSmartHTTPServiceClient(conn),
|
||||
commitFilesService: rpc.NewCommitFilesServiceClient(conn),
|
||||
diffService: rpc.NewDiffServiceClient(conn),
|
||||
mergeService: rpc.NewMergeServiceClient(conn),
|
||||
}, nil
|
||||
}
|
||||
|
@ -38,4 +38,9 @@ type Interface interface {
|
||||
* Diff services
|
||||
*/
|
||||
RawDiff(ctx context.Context, in *RawDiffParams, w io.Writer) error
|
||||
|
||||
/*
|
||||
* Merge services
|
||||
*/
|
||||
MergeBranch(ctx context.Context, in *MergeBranchParams) (string, error)
|
||||
}
|
||||
|
260
gitrpc/internal/gitea/merge.go
Normal file
260
gitrpc/internal/gitea/merge.go
Normal file
@ -0,0 +1,260 @@
|
||||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/harness/gitness/gitrpc/internal/tempdir"
|
||||
"github.com/harness/gitness/gitrpc/internal/types"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
)
|
||||
|
||||
// CreateTemporaryRepo creates a temporary repo with "base" for pr.BaseBranch and "tracking" for pr.HeadBranch
|
||||
// it also create a second base branch called "original_base".
|
||||
//
|
||||
//nolint:funlen,gocognit // need refactor
|
||||
func (g Adapter) CreateTemporaryRepoForPR(
|
||||
ctx context.Context,
|
||||
reposTempPath string,
|
||||
pr *types.PullRequest,
|
||||
) (string, error) {
|
||||
if pr.BaseRepoPath == "" && pr.HeadRepoPath != "" {
|
||||
pr.BaseRepoPath = pr.HeadRepoPath
|
||||
}
|
||||
|
||||
if pr.HeadRepoPath == "" && pr.BaseRepoPath != "" {
|
||||
pr.HeadRepoPath = pr.BaseRepoPath
|
||||
}
|
||||
|
||||
if pr.BaseBranch == "" {
|
||||
return "", errors.New("empty base branch")
|
||||
}
|
||||
|
||||
if pr.HeadBranch == "" {
|
||||
return "", errors.New("empty head branch")
|
||||
}
|
||||
|
||||
baseRepoPath := pr.BaseRepoPath
|
||||
headRepoPath := pr.HeadRepoPath
|
||||
|
||||
// Clone base repo.
|
||||
tmpBasePath, err := tempdir.CreateTemporaryPath(reposTempPath, "pull")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err = g.InitRepository(ctx, tmpBasePath, false); err != nil {
|
||||
// log.Error("git init tmpBasePath: %v", err)
|
||||
_ = tempdir.RemoveTemporaryPath(tmpBasePath)
|
||||
return "", err
|
||||
}
|
||||
|
||||
remoteRepoName := "head_repo"
|
||||
baseBranch := "base"
|
||||
|
||||
// Add head repo remote.
|
||||
addCacheRepo := func(staging, cache string) error {
|
||||
var f *os.File
|
||||
alternates := filepath.Join(staging, ".git", "objects", "info", "alternates")
|
||||
f, err = os.OpenFile(alternates, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
data := filepath.Join(cache, "objects")
|
||||
if _, err = fmt.Fprintln(f, data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = addCacheRepo(tmpBasePath, baseRepoPath); err != nil {
|
||||
_ = tempdir.RemoveTemporaryPath(tmpBasePath)
|
||||
return "", fmt.Errorf("unable to add base repository to temporary repo [%s -> tmpBasePath]: %w", pr.BaseRepoPath, err)
|
||||
}
|
||||
|
||||
var outbuf, errbuf strings.Builder
|
||||
if err = git.NewCommand(ctx, "remote", "add", "-t", pr.BaseBranch, "-m", pr.BaseBranch, "origin", baseRepoPath).
|
||||
Run(&git.RunOpts{
|
||||
Dir: tmpBasePath,
|
||||
Stdout: &outbuf,
|
||||
Stderr: &errbuf,
|
||||
}); err != nil {
|
||||
_ = tempdir.RemoveTemporaryPath(tmpBasePath)
|
||||
return "", fmt.Errorf("unable to add base repository as origin "+
|
||||
"[%s -> tmpBasePath]: %w\n%s\n%s", pr.BaseRepoPath, err, outbuf.String(), errbuf.String())
|
||||
}
|
||||
outbuf.Reset()
|
||||
errbuf.Reset()
|
||||
|
||||
if err = git.NewCommand(ctx, "fetch", "origin", "--no-tags", "--",
|
||||
pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch).
|
||||
Run(&git.RunOpts{
|
||||
Dir: tmpBasePath,
|
||||
Stdout: &outbuf,
|
||||
Stderr: &errbuf,
|
||||
}); err != nil {
|
||||
_ = tempdir.RemoveTemporaryPath(tmpBasePath)
|
||||
return "", fmt.Errorf("unable to fetch origin base branch "+
|
||||
"[%s:%s -> base, original_base in tmpBasePath]: %w\n%s\n%s",
|
||||
pr.BaseRepoPath, pr.BaseBranch, err, outbuf.String(), errbuf.String())
|
||||
}
|
||||
outbuf.Reset()
|
||||
errbuf.Reset()
|
||||
|
||||
if err = git.NewCommand(ctx, "symbolic-ref", "HEAD", git.BranchPrefix+baseBranch).
|
||||
Run(&git.RunOpts{
|
||||
Dir: tmpBasePath,
|
||||
Stdout: &outbuf,
|
||||
Stderr: &errbuf,
|
||||
}); err != nil {
|
||||
_ = tempdir.RemoveTemporaryPath(tmpBasePath)
|
||||
return "", fmt.Errorf("unable to set HEAD as base "+
|
||||
"branch [tmpBasePath]: %w\n%s\n%s", err, outbuf.String(), errbuf.String())
|
||||
}
|
||||
outbuf.Reset()
|
||||
errbuf.Reset()
|
||||
|
||||
if err = addCacheRepo(tmpBasePath, headRepoPath); err != nil {
|
||||
_ = tempdir.RemoveTemporaryPath(tmpBasePath)
|
||||
return "", fmt.Errorf("unable to head base repository "+
|
||||
"to temporary repo [%s -> tmpBasePath]: %w", pr.HeadRepoPath, err)
|
||||
}
|
||||
|
||||
if err = git.NewCommand(ctx, "remote", "add", remoteRepoName, headRepoPath).
|
||||
Run(&git.RunOpts{
|
||||
Dir: tmpBasePath,
|
||||
Stdout: &outbuf,
|
||||
Stderr: &errbuf,
|
||||
}); err != nil {
|
||||
_ = tempdir.RemoveTemporaryPath(tmpBasePath)
|
||||
return "", fmt.Errorf("unable to add head repository as head_repo "+
|
||||
"[%s -> tmpBasePath]: %w\n%s\n%s", pr.HeadRepoPath, err, outbuf.String(), errbuf.String())
|
||||
}
|
||||
outbuf.Reset()
|
||||
errbuf.Reset()
|
||||
|
||||
trackingBranch := "tracking"
|
||||
headBranch := git.BranchPrefix + pr.HeadBranch
|
||||
if err = git.NewCommand(ctx, "fetch", "--no-tags", remoteRepoName, headBranch+":"+trackingBranch).
|
||||
Run(&git.RunOpts{
|
||||
Dir: tmpBasePath,
|
||||
Stdout: &outbuf,
|
||||
Stderr: &errbuf,
|
||||
}); err != nil {
|
||||
_ = tempdir.RemoveTemporaryPath(tmpBasePath)
|
||||
if !git.IsBranchExist(ctx, pr.HeadRepoPath, headBranch) {
|
||||
return "", models.ErrBranchDoesNotExist{
|
||||
BranchName: headBranch,
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("unable to fetch head_repo head branch "+
|
||||
"[%s:%s -> tracking in tmpBasePath]: %w\n%s\n%s",
|
||||
pr.HeadRepoPath, headBranch, err, outbuf.String(), errbuf.String())
|
||||
}
|
||||
outbuf.Reset()
|
||||
errbuf.Reset()
|
||||
|
||||
return tmpBasePath, nil
|
||||
}
|
||||
|
||||
func (g Adapter) Merge(
|
||||
ctx context.Context,
|
||||
pr *types.PullRequest,
|
||||
mergeMethod string,
|
||||
trackingBranch string,
|
||||
tmpBasePath string,
|
||||
env []string,
|
||||
) error {
|
||||
var outbuf, errbuf strings.Builder
|
||||
cmd := git.NewCommand(ctx, "merge", "--no-ff", trackingBranch)
|
||||
if err := cmd.Run(&git.RunOpts{
|
||||
Env: env,
|
||||
Dir: tmpBasePath,
|
||||
Stdout: &outbuf,
|
||||
Stderr: &errbuf,
|
||||
}); err != nil {
|
||||
// Merge will leave a MERGE_HEAD file in the .git folder if there is a conflict
|
||||
if _, statErr := os.Stat(filepath.Join(tmpBasePath, ".git", "MERGE_HEAD")); statErr == nil {
|
||||
// We have a merge conflict error
|
||||
return types.MergeConflictsError{
|
||||
Method: mergeMethod,
|
||||
StdOut: outbuf.String(),
|
||||
StdErr: errbuf.String(),
|
||||
Err: err,
|
||||
}
|
||||
} else if strings.Contains(errbuf.String(), "refusing to merge unrelated histories") {
|
||||
return types.MergeUnrelatedHistoriesError{
|
||||
Method: mergeMethod,
|
||||
StdOut: outbuf.String(),
|
||||
StdErr: errbuf.String(),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("git merge [%s -> %s]: %w\n%s\n%s",
|
||||
pr.HeadBranch, pr.BaseBranch, err, outbuf.String(), errbuf.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g Adapter) GetDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string) (string, error) {
|
||||
getDiffTreeFromBranch := func(repoPath, baseBranch, headBranch string) (string, error) {
|
||||
var outbuf, errbuf strings.Builder
|
||||
// Compute the diff-tree for sparse-checkout
|
||||
if err := git.NewCommand(ctx, "diff-tree", "--no-commit-id",
|
||||
"--name-only", "-r", "-z", "--root", baseBranch, headBranch, "--").
|
||||
Run(&git.RunOpts{
|
||||
Dir: repoPath,
|
||||
Stdout: &outbuf,
|
||||
Stderr: &errbuf,
|
||||
}); err != nil {
|
||||
return "", fmt.Errorf("git diff-tree [%s base:%s head:%s]: %s", repoPath, baseBranch, headBranch, errbuf.String())
|
||||
}
|
||||
return outbuf.String(), nil
|
||||
}
|
||||
|
||||
scanNullTerminatedStrings := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||
if atEOF && len(data) == 0 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
if i := bytes.IndexByte(data, '\x00'); i >= 0 {
|
||||
return i + 1, data[0:i], nil
|
||||
}
|
||||
if atEOF {
|
||||
return len(data), data, nil
|
||||
}
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
list, err := getDiffTreeFromBranch(repoPath, baseBranch, headBranch)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Prefixing '/' for each entry, otherwise all files with the same name in subdirectories would be matched.
|
||||
out := bytes.Buffer{}
|
||||
scanner := bufio.NewScanner(strings.NewReader(list))
|
||||
scanner.Split(scanNullTerminatedStrings)
|
||||
for scanner.Scan() {
|
||||
filepath := scanner.Text()
|
||||
// escape '*', '?', '[', spaces and '!' prefix
|
||||
filepath = escapedSymbols.ReplaceAllString(filepath, `\$1`)
|
||||
// no necessary to escape the first '#' symbol because the first symbol is '/'
|
||||
fmt.Fprintf(&out, "/%s\n", filepath)
|
||||
}
|
||||
|
||||
return out.String(), nil
|
||||
}
|
11
gitrpc/internal/gitea/vars.go
Normal file
11
gitrpc/internal/gitea/vars.go
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package gitea
|
||||
|
||||
import "regexp"
|
||||
|
||||
var (
|
||||
escapedSymbols = regexp.MustCompile(`([*[?! \\])`)
|
||||
)
|
@ -38,4 +38,8 @@ type GitAdapter interface {
|
||||
GetCommitDivergences(ctx context.Context, repoPath string,
|
||||
requests []types.CommitDivergenceRequest, max int32) ([]types.CommitDivergence, error)
|
||||
GetRef(ctx context.Context, repoPath string, name string, refType types.RefType) (string, error)
|
||||
CreateTemporaryRepoForPR(ctx context.Context, reposTempPath string, pr *types.PullRequest) (string, error)
|
||||
Merge(ctx context.Context, pr *types.PullRequest, mergeMethod string, trackingBranch string,
|
||||
tmpBasePath string, env []string) error
|
||||
GetDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string) (string, error)
|
||||
}
|
||||
|
247
gitrpc/internal/service/merge.go
Normal file
247
gitrpc/internal/service/merge.go
Normal file
@ -0,0 +1,247 @@
|
||||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/gitrpc/internal/tempdir"
|
||||
"github.com/harness/gitness/gitrpc/internal/types"
|
||||
"github.com/harness/gitness/gitrpc/rpc"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
)
|
||||
|
||||
type MergeService struct {
|
||||
rpc.UnimplementedMergeServiceServer
|
||||
adapter GitAdapter
|
||||
reposRoot string
|
||||
reposTempDir string
|
||||
}
|
||||
|
||||
var _ rpc.MergeServiceServer = (*MergeService)(nil)
|
||||
|
||||
func NewMergeService(adapter GitAdapter, reposRoot, reposTempDir string) (*MergeService, error) {
|
||||
return &MergeService{
|
||||
adapter: adapter,
|
||||
reposRoot: reposRoot,
|
||||
reposTempDir: reposTempDir,
|
||||
}, nil
|
||||
}
|
||||
|
||||
//nolint:funlen // needs refactor when all merge methods are implemented
|
||||
func (s MergeService) MergeBranch(
|
||||
ctx context.Context,
|
||||
request *rpc.MergeBranchRequest,
|
||||
) (*rpc.MergeBranchResponse, error) {
|
||||
if err := validateMergeBranchRequest(request); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repoPath := getFullPathForRepo(s.reposRoot, request.GetBase().GetRepoUid())
|
||||
pr := &types.PullRequest{
|
||||
BaseRepoPath: repoPath,
|
||||
BaseBranch: request.GetBranch(),
|
||||
HeadBranch: request.GetHeadBranch(),
|
||||
}
|
||||
// Clone base repo.
|
||||
tmpBasePath, err := s.adapter.CreateTemporaryRepoForPR(ctx, s.reposTempDir, pr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = tempdir.RemoveTemporaryPath(tmpBasePath)
|
||||
}()
|
||||
|
||||
var outbuf, errbuf strings.Builder
|
||||
|
||||
baseBranch := "base"
|
||||
trackingBranch := "tracking"
|
||||
|
||||
// Enable sparse-checkout
|
||||
sparseCheckoutList, err := s.adapter.GetDiffTree(ctx, tmpBasePath, baseBranch, trackingBranch)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getDiffTree: %w", err)
|
||||
}
|
||||
|
||||
infoPath := filepath.Join(tmpBasePath, ".git", "info")
|
||||
if err = os.MkdirAll(infoPath, 0o700); err != nil {
|
||||
return nil, fmt.Errorf("unable to create .git/info in tmpBasePath: %w", err)
|
||||
}
|
||||
|
||||
sparseCheckoutListPath := filepath.Join(infoPath, "sparse-checkout")
|
||||
if err = os.WriteFile(sparseCheckoutListPath, []byte(sparseCheckoutList), 0o600); err != nil {
|
||||
return nil, fmt.Errorf("unable to write .git/info/sparse-checkout file in tmpBasePath: %w", err)
|
||||
}
|
||||
|
||||
gitConfigCommand := func() *git.Command {
|
||||
return git.NewCommand(ctx, "config", "--local")
|
||||
}
|
||||
|
||||
// Switch off LFS process (set required, clean and smudge here also)
|
||||
if err = gitConfigCommand().AddArguments("filter.lfs.process", "").
|
||||
Run(&git.RunOpts{
|
||||
Dir: tmpBasePath,
|
||||
Stdout: &outbuf,
|
||||
Stderr: &errbuf,
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("git config [filter.lfs.process -> <> ]: %w\n%s\n%s",
|
||||
err, outbuf.String(), errbuf.String())
|
||||
}
|
||||
outbuf.Reset()
|
||||
errbuf.Reset()
|
||||
|
||||
if err = gitConfigCommand().AddArguments("filter.lfs.required", "false").
|
||||
Run(&git.RunOpts{
|
||||
Dir: tmpBasePath,
|
||||
Stdout: &outbuf,
|
||||
Stderr: &errbuf,
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("git config [filter.lfs.required -> <false> ]: %w\n%s\n%s",
|
||||
err, outbuf.String(), errbuf.String())
|
||||
}
|
||||
outbuf.Reset()
|
||||
errbuf.Reset()
|
||||
|
||||
if err = gitConfigCommand().AddArguments("filter.lfs.clean", "").
|
||||
Run(&git.RunOpts{
|
||||
Dir: tmpBasePath,
|
||||
Stdout: &outbuf,
|
||||
Stderr: &errbuf,
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("git config [filter.lfs.clean -> <> ]: %w\n%s\n%s",
|
||||
err, outbuf.String(), errbuf.String())
|
||||
}
|
||||
outbuf.Reset()
|
||||
errbuf.Reset()
|
||||
|
||||
if err = gitConfigCommand().AddArguments("filter.lfs.smudge", "").
|
||||
Run(&git.RunOpts{
|
||||
Dir: tmpBasePath,
|
||||
Stdout: &outbuf,
|
||||
Stderr: &errbuf,
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("git config [filter.lfs.smudge -> <> ]: %w\n%s\n%s", err, outbuf.String(), errbuf.String())
|
||||
}
|
||||
outbuf.Reset()
|
||||
errbuf.Reset()
|
||||
|
||||
if err = gitConfigCommand().AddArguments("core.sparseCheckout", "true").
|
||||
Run(&git.RunOpts{
|
||||
Dir: tmpBasePath,
|
||||
Stdout: &outbuf,
|
||||
Stderr: &errbuf,
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("git config [core.sparsecheckout -> true]: %w\n%s\n%s", err, outbuf.String(), errbuf.String())
|
||||
}
|
||||
outbuf.Reset()
|
||||
errbuf.Reset()
|
||||
|
||||
// Read base branch index
|
||||
if err = git.NewCommand(ctx, "read-tree", "HEAD").
|
||||
Run(&git.RunOpts{
|
||||
Dir: tmpBasePath,
|
||||
Stdout: &outbuf,
|
||||
Stderr: &errbuf,
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("unable to read base branch in to the index: %w\n%s\n%s",
|
||||
err, outbuf.String(), errbuf.String())
|
||||
}
|
||||
outbuf.Reset()
|
||||
errbuf.Reset()
|
||||
|
||||
sig := &git.Signature{
|
||||
Name: request.GetBase().GetActor().GetName(),
|
||||
Email: request.GetBase().GetActor().GetEmail(),
|
||||
}
|
||||
committer := sig
|
||||
commitTimeStr := time.Now().Format(time.RFC3339)
|
||||
|
||||
// Because this may call hooks we should pass in the environment
|
||||
env := append(CreateEnvironmentForPush(ctx, request.GetBase()),
|
||||
"GIT_AUTHOR_NAME="+sig.Name,
|
||||
"GIT_AUTHOR_EMAIL="+sig.Email,
|
||||
"GIT_AUTHOR_DATE="+commitTimeStr,
|
||||
"GIT_COMMITTER_NAME="+committer.Name,
|
||||
"GIT_COMMITTER_EMAIL="+committer.Email,
|
||||
"GIT_COMMITTER_DATE="+commitTimeStr,
|
||||
)
|
||||
|
||||
if err = s.adapter.Merge(ctx, pr, "merge", trackingBranch, tmpBasePath, env); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mergeCommitID, err := git.GetFullCommitID(ctx, tmpBasePath, baseBranch)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get full commit id for the new merge: %w", err)
|
||||
}
|
||||
|
||||
pushCmd := git.NewCommand(ctx, "push", "origin", baseBranch+":"+git.BranchPrefix+pr.BaseBranch)
|
||||
|
||||
if err = pushCmd.Run(&git.RunOpts{
|
||||
Env: env,
|
||||
Dir: tmpBasePath,
|
||||
Stdout: &outbuf,
|
||||
Stderr: &errbuf,
|
||||
}); err != nil {
|
||||
if strings.Contains(errbuf.String(), "non-fast-forward") {
|
||||
return nil, &git.ErrPushOutOfDate{
|
||||
StdOut: outbuf.String(),
|
||||
StdErr: errbuf.String(),
|
||||
Err: err,
|
||||
}
|
||||
} else if strings.Contains(errbuf.String(), "! [remote rejected]") {
|
||||
err := &git.ErrPushRejected{
|
||||
StdOut: outbuf.String(),
|
||||
StdErr: errbuf.String(),
|
||||
Err: err,
|
||||
}
|
||||
err.GenerateMessage()
|
||||
return nil, err
|
||||
}
|
||||
return nil, fmt.Errorf("git push: %s", errbuf.String())
|
||||
}
|
||||
outbuf.Reset()
|
||||
errbuf.Reset()
|
||||
|
||||
return &rpc.MergeBranchResponse{
|
||||
CommitId: mergeCommitID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func validateMergeBranchRequest(request *rpc.MergeBranchRequest) error {
|
||||
base := request.GetBase()
|
||||
if base == nil {
|
||||
return types.ErrBaseCannotBeEmpty
|
||||
}
|
||||
|
||||
author := base.GetActor()
|
||||
if author == nil {
|
||||
return fmt.Errorf("empty user")
|
||||
}
|
||||
|
||||
if len(author.Email) == 0 {
|
||||
return fmt.Errorf("empty user email")
|
||||
}
|
||||
|
||||
if len(author.Name) == 0 {
|
||||
return fmt.Errorf("empty user name")
|
||||
}
|
||||
|
||||
if len(request.Branch) == 0 {
|
||||
return fmt.Errorf("empty branch name")
|
||||
}
|
||||
|
||||
if request.HeadBranch == "" {
|
||||
return fmt.Errorf("empty head branch name")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -4,7 +4,10 @@
|
||||
|
||||
package types
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAlreadyExists = errors.New("already exists")
|
||||
@ -20,3 +23,27 @@ var (
|
||||
ErrEmptyLeftCommitID = errors.New("empty LeftCommitId")
|
||||
ErrEmptyRightCommitID = errors.New("empty RightCommitId")
|
||||
)
|
||||
|
||||
// MergeConflictsError represents an error if merging fails with a conflict.
|
||||
type MergeConflictsError struct {
|
||||
Method string
|
||||
StdOut string
|
||||
StdErr string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (err MergeConflictsError) Error() string {
|
||||
return fmt.Sprintf("Merge Conflict Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
||||
}
|
||||
|
||||
// MergeUnrelatedHistoriesError represents an error if merging fails due to unrelated histories.
|
||||
type MergeUnrelatedHistoriesError struct {
|
||||
Method string
|
||||
StdOut string
|
||||
StdErr string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (err MergeUnrelatedHistoriesError) Error() string {
|
||||
return fmt.Sprintf("Merge UnrelatedHistories Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
||||
}
|
||||
|
@ -245,3 +245,11 @@ type CommitDivergence struct {
|
||||
// Behind is the count of commits the 'From' ref is behind the 'To' ref.
|
||||
Behind int32
|
||||
}
|
||||
|
||||
type PullRequest struct {
|
||||
BaseRepoPath string
|
||||
HeadRepoPath string
|
||||
|
||||
BaseBranch string
|
||||
HeadBranch string
|
||||
}
|
||||
|
38
gitrpc/merge.go
Normal file
38
gitrpc/merge.go
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package gitrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/gitrpc/rpc"
|
||||
)
|
||||
|
||||
type MergeBranchParams struct {
|
||||
WriteParams
|
||||
BaseBranch string
|
||||
HeadRepoUID string
|
||||
HeadBranch string
|
||||
Force bool
|
||||
DeleteBranch bool
|
||||
}
|
||||
|
||||
func (c *Client) MergeBranch(ctx context.Context, params *MergeBranchParams) (string, error) {
|
||||
if params == nil {
|
||||
return "", ErrNoParamsProvided
|
||||
}
|
||||
|
||||
resp, err := c.mergeService.MergeBranch(ctx, &rpc.MergeBranchRequest{
|
||||
Base: mapToRPCWriteRequest(params.WriteParams),
|
||||
Branch: params.BaseBranch,
|
||||
HeadBranch: params.HeadBranch,
|
||||
Force: params.Force,
|
||||
Delete: params.DeleteBranch,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.CommitId, nil
|
||||
}
|
36
gitrpc/proto/merge.proto
Normal file
36
gitrpc/proto/merge.proto
Normal file
@ -0,0 +1,36 @@
|
||||
syntax = "proto3";
|
||||
package rpc;
|
||||
|
||||
option go_package = "github.com/harness/gitness/gitrpc/rpc";
|
||||
|
||||
import "shared.proto";
|
||||
|
||||
// DiffService is a service which provides RPCs to inspect differences
|
||||
// introduced between a set of commits.
|
||||
service MergeService {
|
||||
rpc MergeBranch(MergeBranchRequest) returns (MergeBranchResponse) {}
|
||||
}
|
||||
|
||||
|
||||
message MergeBranchRequest {
|
||||
WriteRequest base = 1;
|
||||
// head_branch is the source branch we want to merge
|
||||
string head_branch = 2;
|
||||
// branch is the branch into which the given commit shall be merged and whose
|
||||
// reference is going to be updated.
|
||||
string branch = 3;
|
||||
// title is the title to use for the merge commit.
|
||||
string title = 4;
|
||||
// message is the message to use for the merge commit.
|
||||
string message = 5;
|
||||
// force merge
|
||||
bool force = 6;
|
||||
// delete branch after merge
|
||||
bool delete = 7;
|
||||
}
|
||||
|
||||
// This comment is left unintentionally blank.
|
||||
message MergeBranchResponse {
|
||||
// The merge commit the branch will be updated to. The caller can still abort the merge.
|
||||
string commit_id = 1;
|
||||
}
|
@ -67,4 +67,4 @@ message CommitFilesRequest {
|
||||
message CommitFilesResponse {
|
||||
string commit_id = 1;
|
||||
string branch = 2;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.11
|
||||
// protoc v3.21.9
|
||||
// source: diff.proto
|
||||
|
||||
package rpc
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.11
|
||||
// - protoc v3.21.9
|
||||
// source: diff.proto
|
||||
|
||||
package rpc
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.11
|
||||
// protoc v3.21.9
|
||||
// source: http.proto
|
||||
|
||||
package rpc
|
||||
@ -151,6 +151,7 @@ type ServicePackRequest struct {
|
||||
// Depending on the service the matching base type has to be passed
|
||||
//
|
||||
// Types that are assignable to Base:
|
||||
//
|
||||
// *ServicePackRequest_ReadBase
|
||||
// *ServicePackRequest_WriteBase
|
||||
Base isServicePackRequest_Base `protobuf_oneof:"base"`
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.11
|
||||
// - protoc v3.21.9
|
||||
// source: http.proto
|
||||
|
||||
package rpc
|
||||
|
285
gitrpc/rpc/merge.pb.go
Normal file
285
gitrpc/rpc/merge.pb.go
Normal file
@ -0,0 +1,285 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.9
|
||||
// source: merge.proto
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type MergeBranchRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Base *WriteRequest `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
|
||||
// head_branch is the source branch we want to merge
|
||||
HeadBranch string `protobuf:"bytes,2,opt,name=head_branch,json=headBranch,proto3" json:"head_branch,omitempty"`
|
||||
// branch is the branch into which the given commit shall be merged and whose
|
||||
// reference is going to be updated.
|
||||
Branch string `protobuf:"bytes,3,opt,name=branch,proto3" json:"branch,omitempty"`
|
||||
// title is the title to use for the merge commit.
|
||||
Title string `protobuf:"bytes,4,opt,name=title,proto3" json:"title,omitempty"`
|
||||
// message is the message to use for the merge commit.
|
||||
Message string `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"`
|
||||
// force merge
|
||||
Force bool `protobuf:"varint,6,opt,name=force,proto3" json:"force,omitempty"`
|
||||
// delete branch after merge
|
||||
Delete bool `protobuf:"varint,7,opt,name=delete,proto3" json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
func (x *MergeBranchRequest) Reset() {
|
||||
*x = MergeBranchRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_merge_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *MergeBranchRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*MergeBranchRequest) ProtoMessage() {}
|
||||
|
||||
func (x *MergeBranchRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_merge_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use MergeBranchRequest.ProtoReflect.Descriptor instead.
|
||||
func (*MergeBranchRequest) Descriptor() ([]byte, []int) {
|
||||
return file_merge_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *MergeBranchRequest) GetBase() *WriteRequest {
|
||||
if x != nil {
|
||||
return x.Base
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *MergeBranchRequest) GetHeadBranch() string {
|
||||
if x != nil {
|
||||
return x.HeadBranch
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *MergeBranchRequest) GetBranch() string {
|
||||
if x != nil {
|
||||
return x.Branch
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *MergeBranchRequest) GetTitle() string {
|
||||
if x != nil {
|
||||
return x.Title
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *MergeBranchRequest) GetMessage() string {
|
||||
if x != nil {
|
||||
return x.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *MergeBranchRequest) GetForce() bool {
|
||||
if x != nil {
|
||||
return x.Force
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *MergeBranchRequest) GetDelete() bool {
|
||||
if x != nil {
|
||||
return x.Delete
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// This comment is left unintentionally blank.
|
||||
type MergeBranchResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// The merge commit the branch will be updated to. The caller can still abort the merge.
|
||||
CommitId string `protobuf:"bytes,1,opt,name=commit_id,json=commitId,proto3" json:"commit_id,omitempty"`
|
||||
}
|
||||
|
||||
func (x *MergeBranchResponse) Reset() {
|
||||
*x = MergeBranchResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_merge_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *MergeBranchResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*MergeBranchResponse) ProtoMessage() {}
|
||||
|
||||
func (x *MergeBranchResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_merge_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use MergeBranchResponse.ProtoReflect.Descriptor instead.
|
||||
func (*MergeBranchResponse) Descriptor() ([]byte, []int) {
|
||||
return file_merge_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *MergeBranchResponse) GetCommitId() string {
|
||||
if x != nil {
|
||||
return x.CommitId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_merge_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_merge_proto_rawDesc = []byte{
|
||||
0x0a, 0x0b, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x72,
|
||||
0x70, 0x63, 0x1a, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x22, 0xd2, 0x01, 0x0a, 0x12, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x72, 0x69, 0x74,
|
||||
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x1f,
|
||||
0x0a, 0x0b, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x0a, 0x68, 0x65, 0x61, 0x64, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12,
|
||||
0x16, 0x0a, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x18, 0x0a,
|
||||
0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
||||
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65,
|
||||
0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a,
|
||||
0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64,
|
||||
0x65, 0x6c, 0x65, 0x74, 0x65, 0x22, 0x32, 0x0a, 0x13, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x42, 0x72,
|
||||
0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09,
|
||||
0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x49, 0x64, 0x32, 0x52, 0x0a, 0x0c, 0x4d, 0x65, 0x72,
|
||||
0x67, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x42, 0x0a, 0x0b, 0x4d, 0x65, 0x72,
|
||||
0x67, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x17, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4d,
|
||||
0x65, 0x72, 0x67, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x18, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x42, 0x72, 0x61,
|
||||
0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x27, 0x5a,
|
||||
0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x72, 0x6e,
|
||||
0x65, 0x73, 0x73, 0x2f, 0x67, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x69, 0x74, 0x72,
|
||||
0x70, 0x63, 0x2f, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_merge_proto_rawDescOnce sync.Once
|
||||
file_merge_proto_rawDescData = file_merge_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_merge_proto_rawDescGZIP() []byte {
|
||||
file_merge_proto_rawDescOnce.Do(func() {
|
||||
file_merge_proto_rawDescData = protoimpl.X.CompressGZIP(file_merge_proto_rawDescData)
|
||||
})
|
||||
return file_merge_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_merge_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_merge_proto_goTypes = []interface{}{
|
||||
(*MergeBranchRequest)(nil), // 0: rpc.MergeBranchRequest
|
||||
(*MergeBranchResponse)(nil), // 1: rpc.MergeBranchResponse
|
||||
(*WriteRequest)(nil), // 2: rpc.WriteRequest
|
||||
}
|
||||
var file_merge_proto_depIdxs = []int32{
|
||||
2, // 0: rpc.MergeBranchRequest.base:type_name -> rpc.WriteRequest
|
||||
0, // 1: rpc.MergeService.MergeBranch:input_type -> rpc.MergeBranchRequest
|
||||
1, // 2: rpc.MergeService.MergeBranch:output_type -> rpc.MergeBranchResponse
|
||||
2, // [2:3] is the sub-list for method output_type
|
||||
1, // [1:2] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_merge_proto_init() }
|
||||
func file_merge_proto_init() {
|
||||
if File_merge_proto != nil {
|
||||
return
|
||||
}
|
||||
file_shared_proto_init()
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_merge_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*MergeBranchRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_merge_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*MergeBranchResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_merge_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_merge_proto_goTypes,
|
||||
DependencyIndexes: file_merge_proto_depIdxs,
|
||||
MessageInfos: file_merge_proto_msgTypes,
|
||||
}.Build()
|
||||
File_merge_proto = out.File
|
||||
file_merge_proto_rawDesc = nil
|
||||
file_merge_proto_goTypes = nil
|
||||
file_merge_proto_depIdxs = nil
|
||||
}
|
105
gitrpc/rpc/merge_grpc.pb.go
Normal file
105
gitrpc/rpc/merge_grpc.pb.go
Normal file
@ -0,0 +1,105 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.9
|
||||
// source: merge.proto
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// MergeServiceClient is the client API for MergeService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type MergeServiceClient interface {
|
||||
MergeBranch(ctx context.Context, in *MergeBranchRequest, opts ...grpc.CallOption) (*MergeBranchResponse, error)
|
||||
}
|
||||
|
||||
type mergeServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewMergeServiceClient(cc grpc.ClientConnInterface) MergeServiceClient {
|
||||
return &mergeServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *mergeServiceClient) MergeBranch(ctx context.Context, in *MergeBranchRequest, opts ...grpc.CallOption) (*MergeBranchResponse, error) {
|
||||
out := new(MergeBranchResponse)
|
||||
err := c.cc.Invoke(ctx, "/rpc.MergeService/MergeBranch", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// MergeServiceServer is the server API for MergeService service.
|
||||
// All implementations must embed UnimplementedMergeServiceServer
|
||||
// for forward compatibility
|
||||
type MergeServiceServer interface {
|
||||
MergeBranch(context.Context, *MergeBranchRequest) (*MergeBranchResponse, error)
|
||||
mustEmbedUnimplementedMergeServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedMergeServiceServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedMergeServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedMergeServiceServer) MergeBranch(context.Context, *MergeBranchRequest) (*MergeBranchResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method MergeBranch not implemented")
|
||||
}
|
||||
func (UnimplementedMergeServiceServer) mustEmbedUnimplementedMergeServiceServer() {}
|
||||
|
||||
// UnsafeMergeServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to MergeServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeMergeServiceServer interface {
|
||||
mustEmbedUnimplementedMergeServiceServer()
|
||||
}
|
||||
|
||||
func RegisterMergeServiceServer(s grpc.ServiceRegistrar, srv MergeServiceServer) {
|
||||
s.RegisterService(&MergeService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _MergeService_MergeBranch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(MergeBranchRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MergeServiceServer).MergeBranch(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/rpc.MergeService/MergeBranch",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MergeServiceServer).MergeBranch(ctx, req.(*MergeBranchRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// MergeService_ServiceDesc is the grpc.ServiceDesc for MergeService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var MergeService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "rpc.MergeService",
|
||||
HandlerType: (*MergeServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "MergeBranch",
|
||||
Handler: _MergeService_MergeBranch_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "merge.proto",
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.11
|
||||
// protoc v3.21.9
|
||||
// source: operations.proto
|
||||
|
||||
package rpc
|
||||
@ -239,6 +239,7 @@ type CommitFilesAction struct {
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Types that are assignable to Payload:
|
||||
//
|
||||
// *CommitFilesAction_Header
|
||||
// *CommitFilesAction_Content
|
||||
Payload isCommitFilesAction_Payload `protobuf_oneof:"payload"`
|
||||
@ -322,6 +323,7 @@ type CommitFilesRequest struct {
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Types that are assignable to Payload:
|
||||
//
|
||||
// *CommitFilesRequest_Header
|
||||
// *CommitFilesRequest_Action
|
||||
Payload isCommitFilesRequest_Payload `protobuf_oneof:"payload"`
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.11
|
||||
// - protoc v3.21.9
|
||||
// source: operations.proto
|
||||
|
||||
package rpc
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.11
|
||||
// protoc v3.21.9
|
||||
// source: ref.proto
|
||||
|
||||
package rpc
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.11
|
||||
// - protoc v3.21.9
|
||||
// source: ref.proto
|
||||
|
||||
package rpc
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.11
|
||||
// protoc v3.21.9
|
||||
// source: repo.proto
|
||||
|
||||
package rpc
|
||||
@ -130,6 +130,7 @@ type CreateRepositoryRequest struct {
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Types that are assignable to Data:
|
||||
//
|
||||
// *CreateRepositoryRequest_Header
|
||||
// *CreateRepositoryRequest_File
|
||||
Data isCreateRepositoryRequest_Data `protobuf_oneof:"data"`
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.11
|
||||
// - protoc v3.21.9
|
||||
// source: repo.proto
|
||||
|
||||
package rpc
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.11
|
||||
// protoc v3.21.9
|
||||
// source: shared.proto
|
||||
|
||||
package rpc
|
||||
@ -240,6 +240,7 @@ type FileUpload struct {
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Types that are assignable to Data:
|
||||
//
|
||||
// *FileUpload_Header
|
||||
// *FileUpload_Chunk
|
||||
Data isFileUpload_Data `protobuf_oneof:"data"`
|
||||
|
@ -86,6 +86,10 @@ func NewServer(config Config) (*Server, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mergeService, err := service.NewMergeService(adapter, reposRoot, config.TmpDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// register services
|
||||
rpc.RegisterRepositoryServiceServer(s, repoService)
|
||||
@ -93,6 +97,7 @@ func NewServer(config Config) (*Server, error) {
|
||||
rpc.RegisterSmartHTTPServiceServer(s, httpService)
|
||||
rpc.RegisterCommitFilesServiceServer(s, commitFilesService)
|
||||
rpc.RegisterDiffServiceServer(s, diffService)
|
||||
rpc.RegisterMergeServiceServer(s, mergeService)
|
||||
|
||||
return &Server{
|
||||
Server: s,
|
||||
|
106
go.mod
106
go.mod
@ -31,13 +31,15 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.14
|
||||
github.com/mattn/go-sqlite3 v1.14.12
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rs/xid v1.4.0
|
||||
github.com/rs/zerolog v1.26.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/swaggest/openapi-go v0.2.23
|
||||
github.com/swaggest/swgui v1.4.2
|
||||
github.com/unrolled/secure v1.0.8
|
||||
go.uber.org/multierr v1.8.0
|
||||
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122
|
||||
golang.org/x/crypto v0.1.0
|
||||
golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a
|
||||
golang.org/x/sync v0.1.0
|
||||
golang.org/x/term v0.2.0
|
||||
google.golang.org/grpc v1.43.0
|
||||
@ -46,58 +48,152 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.99.0 // indirect
|
||||
gitea.com/go-chi/cache v0.2.0 // indirect
|
||||
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/alecthomas/chroma v0.10.0 // indirect
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/speakeasy v0.1.0 // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cloudflare/cfssl v1.6.1 // indirect
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.12.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/djherbis/buffer v1.2.0 // indirect
|
||||
github.com/djherbis/nio/v3 v3.0.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/duo-labs/webauthn v0.0.0-20220330035159-03696f3d4499 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/envoyproxy/go-control-plane v0.10.1 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
|
||||
github.com/fullstorydev/grpcurl v1.8.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
||||
github.com/go-enry/go-enry/v2 v2.8.2 // indirect
|
||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.3.1 // indirect
|
||||
github.com/go-git/go-git/v5 v5.4.3-0.20210630082519-b4368b2a2ca4 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/goccy/go-json v0.9.7 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.4.1 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/certificate-transparency-go v1.1.2-0.20210511102531-373a877eec92 // indirect
|
||||
github.com/google/pprof v0.0.0-20220509035851-59ca7ad80af3 // indirect
|
||||
github.com/google/subcommands v1.0.1 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
github.com/hashicorp/go-version v1.4.0 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/jackc/pgx/v4 v4.12.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jhump/protoreflect v1.8.2 // indirect
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 // indirect
|
||||
github.com/klauspost/compress v1.15.3 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.19 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minio-go/v7 v7.0.26 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/oliamb/cutter v0.2.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rs/xid v1.4.0 // indirect
|
||||
github.com/pquerna/otp v1.3.0 // indirect
|
||||
github.com/prometheus/client_golang v1.12.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect
|
||||
github.com/sergi/go-diff v1.2.0 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/soheilhy/cmux v0.1.5 // indirect
|
||||
github.com/spf13/cobra v1.3.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/swaggest/jsonschema-go v0.3.40 // indirect
|
||||
github.com/swaggest/refl v1.1.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
|
||||
github.com/unknwon/com v1.0.1 // indirect
|
||||
github.com/urfave/cli v1.22.9 // indirect
|
||||
github.com/vearutop/statigz v1.1.5 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.1 // indirect
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
|
||||
github.com/yohcop/openid-go v1.0.0 // indirect
|
||||
github.com/yuin/goldmark v1.4.13 // indirect
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 // indirect
|
||||
github.com/yuin/goldmark-meta v1.1.0 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.1 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect
|
||||
go.etcd.io/etcd/client/v2 v2.305.1 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/etcdctl/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/raft/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/server/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/tests/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/v3 v3.5.0-alpha.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
|
||||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
mvdan.cc/xurls/v2 v2.4.0 // indirect
|
||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 // indirect
|
||||
xorm.io/builder v0.3.11 // indirect
|
||||
xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f // indirect
|
||||
)
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/harness/gitness/internal/auth"
|
||||
"github.com/harness/gitness/internal/auth/authz"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/internal/url"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
@ -24,6 +25,7 @@ import (
|
||||
|
||||
type Controller struct {
|
||||
db *sqlx.DB
|
||||
urlProvider *url.Provider
|
||||
authorizer authz.Authorizer
|
||||
pullreqStore store.PullReqStore
|
||||
activityStore store.PullReqActivityStore
|
||||
@ -36,6 +38,7 @@ type Controller struct {
|
||||
|
||||
func NewController(
|
||||
db *sqlx.DB,
|
||||
urlProvider *url.Provider,
|
||||
authorizer authz.Authorizer,
|
||||
pullreqStore store.PullReqStore,
|
||||
pullreqActivityStore store.PullReqActivityStore,
|
||||
@ -47,6 +50,7 @@ func NewController(
|
||||
) *Controller {
|
||||
return &Controller{
|
||||
db: db,
|
||||
urlProvider: urlProvider,
|
||||
authorizer: authorizer,
|
||||
pullreqStore: pullreqStore,
|
||||
activityStore: pullreqActivityStore,
|
||||
|
163
internal/api/controller/pullreq/merge.go
Normal file
163
internal/api/controller/pullreq/merge.go
Normal file
@ -0,0 +1,163 @@
|
||||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package pullreq
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/gitrpc"
|
||||
"github.com/harness/gitness/internal/api/controller"
|
||||
"github.com/harness/gitness/internal/api/usererror"
|
||||
"github.com/harness/gitness/internal/auth"
|
||||
"github.com/harness/gitness/internal/store/database/dbtx"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type MergeInput struct {
|
||||
Method enum.MergeMethod `json:"method"`
|
||||
Force bool `json:"force,omitempty"`
|
||||
DeleteBranch bool `json:"delete_branch,omitempty"`
|
||||
}
|
||||
|
||||
// Create creates a new pull request.
|
||||
//
|
||||
//nolint:gocognit // no need to refactor
|
||||
func (c *Controller) Merge(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
pullreqNum int64,
|
||||
in *MergeInput,
|
||||
) (types.MergeResponse, error) {
|
||||
var (
|
||||
sha string
|
||||
pr *types.PullReq
|
||||
activity *types.PullReqActivity
|
||||
)
|
||||
|
||||
if in.Method == "" {
|
||||
in.Method = enum.MergeMethodMerge
|
||||
}
|
||||
|
||||
validMethods := []enum.MergeMethod{enum.MergeMethodMerge, enum.MergeMethodRebase, enum.MergeMethodSquash}
|
||||
if !slices.Contains(validMethods, in.Method) {
|
||||
return types.MergeResponse{}, usererror.BadRequest(
|
||||
fmt.Sprintf("wrong merge method type: %s", in.Method))
|
||||
}
|
||||
|
||||
now := time.Now().UnixMilli()
|
||||
|
||||
targetRepo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit)
|
||||
if err != nil {
|
||||
return types.MergeResponse{}, usererror.BadRequest(
|
||||
fmt.Sprintf("failed to acquire access to target repo: %s", err))
|
||||
}
|
||||
|
||||
err = dbtx.New(c.db).WithTx(ctx, func(ctx context.Context) error {
|
||||
// pesimistic lock for no other user can merge the same pr
|
||||
pr, err = c.pullreqStore.FindByNumberWithLock(ctx, targetRepo.ID, pullreqNum, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get pull request by number: %w", err)
|
||||
}
|
||||
|
||||
if pr.Merged != nil {
|
||||
return errors.New("pull request already merged")
|
||||
}
|
||||
|
||||
if pr.State != enum.PullReqStateOpen {
|
||||
return fmt.Errorf("pull request state cannot be %v", pr.State)
|
||||
}
|
||||
|
||||
sourceRepo := targetRepo
|
||||
if pr.SourceRepoID != pr.TargetRepoID {
|
||||
sourceRepo, err = c.repoStore.Find(ctx, pr.SourceRepoID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get source repository: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var writeParams gitrpc.WriteParams
|
||||
writeParams, err = controller.CreateRPCWriteParams(ctx, c.urlProvider, session, targetRepo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create RPC write params: %w", err)
|
||||
}
|
||||
|
||||
sha, err = c.gitRPCClient.MergeBranch(ctx, &gitrpc.MergeBranchParams{
|
||||
WriteParams: writeParams,
|
||||
BaseBranch: pr.TargetBranch,
|
||||
HeadRepoUID: sourceRepo.GitUID,
|
||||
HeadBranch: pr.SourceBranch,
|
||||
Force: in.Force,
|
||||
DeleteBranch: in.DeleteBranch,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
activity = getMergeActivity(session, pr, in, sha)
|
||||
|
||||
pr.MergeStrategy = &in.Method
|
||||
pr.Merged = &now
|
||||
pr.MergedBy = &session.Principal.ID
|
||||
pr.State = enum.PullReqStateMerged
|
||||
|
||||
err = c.pullreqStore.Update(ctx, pr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update pull request: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return types.MergeResponse{}, err
|
||||
}
|
||||
|
||||
err = c.writeActivity(ctx, pr, activity)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to write pull req activity")
|
||||
}
|
||||
|
||||
return types.MergeResponse{
|
||||
SHA: sha,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getMergeActivity(session *auth.Session, pr *types.PullReq, in *MergeInput, sha string) *types.PullReqActivity {
|
||||
now := time.Now().UnixMilli()
|
||||
|
||||
act := &types.PullReqActivity{
|
||||
ID: 0, // Will be populated in the data layer
|
||||
Version: 0,
|
||||
CreatedBy: session.Principal.ID,
|
||||
Created: now,
|
||||
Updated: now,
|
||||
Edited: now,
|
||||
Deleted: nil,
|
||||
RepoID: pr.TargetRepoID,
|
||||
PullReqID: pr.ID,
|
||||
Order: 0, // Will be filled in writeActivity
|
||||
SubOrder: 0,
|
||||
ReplySeq: 0,
|
||||
Type: enum.PullReqActivityTypeMerge,
|
||||
Kind: enum.PullReqActivityKindSystem,
|
||||
Text: "",
|
||||
Payload: map[string]interface{}{
|
||||
"merge_method": in.Method,
|
||||
"sha": sha,
|
||||
},
|
||||
Metadata: nil,
|
||||
ResolvedBy: nil,
|
||||
Resolved: nil,
|
||||
}
|
||||
|
||||
return act
|
||||
}
|
@ -8,6 +8,7 @@ import (
|
||||
"github.com/harness/gitness/gitrpc"
|
||||
"github.com/harness/gitness/internal/auth/authz"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/internal/url"
|
||||
|
||||
"github.com/google/wire"
|
||||
"github.com/jmoiron/sqlx"
|
||||
@ -18,12 +19,12 @@ var WireSet = wire.NewSet(
|
||||
ProvideController,
|
||||
)
|
||||
|
||||
func ProvideController(db *sqlx.DB, authorizer authz.Authorizer,
|
||||
func ProvideController(db *sqlx.DB, urlProvider *url.Provider, authorizer authz.Authorizer,
|
||||
pullReqStore store.PullReqStore, pullReqActivityStore store.PullReqActivityStore,
|
||||
pullReqReviewStore store.PullReqReviewStore, pullReqReviewerStore store.PullReqReviewerStore,
|
||||
repoStore store.RepoStore, principalStore store.PrincipalStore,
|
||||
rpcClient gitrpc.Interface) *Controller {
|
||||
return NewController(db, authorizer,
|
||||
return NewController(db, urlProvider, authorizer,
|
||||
pullReqStore, pullReqActivityStore,
|
||||
pullReqReviewStore, pullReqReviewerStore,
|
||||
repoStore, principalStore,
|
||||
|
52
internal/api/controller/util.go
Normal file
52
internal/api/controller/util.go
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/gitrpc"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/auth"
|
||||
"github.com/harness/gitness/internal/githook"
|
||||
"github.com/harness/gitness/internal/url"
|
||||
"github.com/harness/gitness/types"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// CreateRPCWriteParams creates base write parameters for gitrpc write operations.
|
||||
// IMPORTANT: session & repo are assumed to be not nil!
|
||||
// TODO: this is duplicate function from repo controller, we need to see where this
|
||||
// function will be best fit.
|
||||
func CreateRPCWriteParams(ctx context.Context, urlProvider *url.Provider,
|
||||
session *auth.Session, repo *types.Repository) (gitrpc.WriteParams, error) {
|
||||
requestID, ok := request.RequestIDFrom(ctx)
|
||||
if !ok {
|
||||
// best effort retrieving of requestID - log in case we can't find it but don't fail operation.
|
||||
log.Ctx(ctx).Warn().Msg("operation doesn't have a requestID in the context.")
|
||||
}
|
||||
|
||||
// generate envars (add everything githook CLI needs for execution)
|
||||
envVars, err := githook.GenerateEnvironmentVariables(&githook.Payload{
|
||||
BaseURL: urlProvider.GetAPIBaseURLInternal(),
|
||||
RepoID: repo.ID,
|
||||
PrincipalID: session.Principal.ID,
|
||||
RequestID: requestID,
|
||||
})
|
||||
if err != nil {
|
||||
return gitrpc.WriteParams{}, fmt.Errorf("failed to generate git hook environment variables: %w", err)
|
||||
}
|
||||
|
||||
return gitrpc.WriteParams{
|
||||
Actor: gitrpc.Identity{
|
||||
Name: session.Principal.DisplayName,
|
||||
Email: session.Principal.Email,
|
||||
},
|
||||
RepoUID: repo.GitUID,
|
||||
EnvVars: envVars,
|
||||
}, nil
|
||||
}
|
51
internal/api/handler/pullreq/merge.go
Normal file
51
internal/api/handler/pullreq/merge.go
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package pullreq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/controller/pullreq"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
)
|
||||
|
||||
// HandleCreate returns a http.HandlerFunc that creates a new pull request.
|
||||
func HandleMerge(pullreqCtrl *pullreq.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
in := new(pullreq.MergeInput)
|
||||
err = json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil && !errors.Is(err, io.EOF) { // allow empty body
|
||||
render.BadRequestf(w, "Invalid Request Body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
pullreqNumber, err := request.GetPullReqNumberFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
pr, err := pullreqCtrl.Merge(ctx, session, repoRef, pullreqNumber, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, pr)
|
||||
}
|
||||
}
|
@ -44,6 +44,11 @@ type listPullReqActivitiesRequest struct {
|
||||
pullReqRequest
|
||||
}
|
||||
|
||||
type mergePullReq struct {
|
||||
pullReqRequest
|
||||
pullreq.MergeInput
|
||||
}
|
||||
|
||||
type commentCreatePullReqRequest struct {
|
||||
pullReqRequest
|
||||
pullreq.CommentCreateInput
|
||||
@ -395,4 +400,18 @@ func pullReqOperations(reflector *openapi3.Reflector) {
|
||||
_ = reflector.SetJSONResponse(&reviewSubmit, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPost,
|
||||
"/repos/{repo_ref}/pullreq/{pullreq_number}/review", reviewSubmit)
|
||||
mergePullReqOp := openapi3.Operation{}
|
||||
mergePullReqOp.WithTags("pullreq")
|
||||
mergePullReqOp.WithMapOfAnything(map[string]interface{}{"operationId": "mergePullReqOp"})
|
||||
_ = reflector.SetRequest(&mergePullReqOp, new(mergePullReq), http.MethodPost)
|
||||
_ = reflector.SetJSONResponse(&mergePullReqOp, new(types.MergeResponse), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&mergePullReqOp, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&mergePullReqOp, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&mergePullReqOp, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&mergePullReqOp, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.SetJSONResponse(&mergePullReqOp, new(usererror.Error), http.StatusMethodNotAllowed)
|
||||
_ = reflector.SetJSONResponse(&mergePullReqOp, new(usererror.Error), http.StatusConflict)
|
||||
_ = reflector.SetJSONResponse(&mergePullReqOp, new(usererror.Error), http.StatusUnprocessableEntity)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPost,
|
||||
"/repos/{repo_ref}/pullreq/{pullreq_number}/comments/{pullreq_comment_id}", mergePullReqOp)
|
||||
}
|
||||
|
@ -247,6 +247,7 @@ func SetupPullReq(r chi.Router, pullreqCtrl *pullreq.Controller) {
|
||||
r.Route("/reviews", func(r chi.Router) {
|
||||
r.Post("/", handlerpullreq.HandleReviewSubmit(pullreqCtrl))
|
||||
})
|
||||
r.Post("/merge", handlerpullreq.HandleMerge(pullreqCtrl))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/internal/store"
|
||||
@ -119,10 +120,19 @@ func (s *PullReqStore) Find(ctx context.Context, id int64) (*types.PullReq, erro
|
||||
}
|
||||
|
||||
// FindByNumber finds the pull request by repo ID and pull request number.
|
||||
func (s *PullReqStore) FindByNumber(ctx context.Context, repoID, number int64) (*types.PullReq, error) {
|
||||
const sqlQuery = pullReqSelectBase + `
|
||||
func (s *PullReqStore) FindByNumberWithLock(
|
||||
ctx context.Context,
|
||||
repoID,
|
||||
number int64,
|
||||
lock bool,
|
||||
) (*types.PullReq, error) {
|
||||
sqlQuery := pullReqSelectBase + `
|
||||
WHERE pullreq_target_repo_id = $1 AND pullreq_number = $2`
|
||||
|
||||
if lock && !strings.HasPrefix(s.db.DriverName(), "sqlite") {
|
||||
sqlQuery += "\nFOR UPDATE"
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
dst := &pullReq{}
|
||||
@ -133,6 +143,11 @@ func (s *PullReqStore) FindByNumber(ctx context.Context, repoID, number int64) (
|
||||
return mapPullReq(dst), nil
|
||||
}
|
||||
|
||||
// FindByNumber finds the pull request by repo ID and pull request number.
|
||||
func (s *PullReqStore) FindByNumber(ctx context.Context, repoID, number int64) (*types.PullReq, error) {
|
||||
return s.FindByNumberWithLock(ctx, repoID, number, false)
|
||||
}
|
||||
|
||||
// Create creates a new pull request.
|
||||
func (s *PullReqStore) Create(ctx context.Context, pr *types.PullReq) error {
|
||||
const sqlQuery = `
|
||||
@ -402,7 +417,7 @@ func mapPullReq(pr *pullReq) *types.PullReq {
|
||||
ActivitySeq: pr.ActivitySeq,
|
||||
MergedBy: pr.MergedBy.Ptr(),
|
||||
Merged: pr.Merged.Ptr(),
|
||||
MergeStrategy: pr.MergeStrategy.Ptr(),
|
||||
MergeStrategy: (*enum.MergeMethod)(pr.MergeStrategy.Ptr()),
|
||||
Author: types.PrincipalInfo{},
|
||||
Merger: nil,
|
||||
}
|
||||
@ -443,7 +458,7 @@ func mapInternalPullReq(pr *types.PullReq) *pullReq {
|
||||
ActivitySeq: pr.ActivitySeq,
|
||||
MergedBy: null.IntFromPtr(pr.MergedBy),
|
||||
Merged: null.IntFromPtr(pr.Merged),
|
||||
MergeStrategy: null.StringFromPtr(pr.MergeStrategy),
|
||||
MergeStrategy: null.StringFromPtr((*string)(pr.MergeStrategy)),
|
||||
}
|
||||
|
||||
return m
|
||||
|
@ -231,6 +231,9 @@ type (
|
||||
// Find the pull request by id.
|
||||
Find(ctx context.Context, id int64) (*types.PullReq, error)
|
||||
|
||||
// FindByNumber finds the pull request by repo ID and the pull request number.
|
||||
FindByNumberWithLock(ctx context.Context, repoID, number int64, lock bool) (*types.PullReq, error)
|
||||
|
||||
// FindByNumber finds the pull request by repo ID and the pull request number.
|
||||
FindByNumber(ctx context.Context, repoID, number int64) (*types.PullReq, error)
|
||||
|
||||
|
@ -8,9 +8,10 @@ import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
user "github.com/harness/gitness/internal/api/controller/user"
|
||||
types "github.com/harness/gitness/types"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
// MockClient is a mock of Client interface.
|
||||
|
@ -8,9 +8,10 @@ import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
types "github.com/harness/gitness/types"
|
||||
enum "github.com/harness/gitness/types/enum"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
// MockSystemStore is a mock of SystemStore interface.
|
||||
|
@ -71,6 +71,7 @@ const (
|
||||
PullReqActivityTypeCodeComment PullReqActivityType = "code-comment"
|
||||
PullReqActivityTypeTitleChange PullReqActivityType = "title-change"
|
||||
PullReqActivityTypeReviewSubmit PullReqActivityType = "review-submit"
|
||||
PullReqActivityTypeMerge PullReqActivityType = "merge"
|
||||
)
|
||||
|
||||
func GetAllPullReqActivityTypes() []PullReqActivityType {
|
||||
@ -179,3 +180,15 @@ func ParsePullReqReviewerType(s string) (PullReqReviewerType, bool) {
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// MergeMethod represents the approach to merge commits into base branch.
|
||||
type MergeMethod string
|
||||
|
||||
const (
|
||||
// MergeMethodMerge create merge commit.
|
||||
MergeMethodMerge MergeMethod = "merge"
|
||||
// MergeMethodSquash squash commits into single commit before merging.
|
||||
MergeMethodSquash MergeMethod = "squash"
|
||||
// MergeMethodRebase rebase before merging.
|
||||
MergeMethodRebase MergeMethod = "rebase"
|
||||
)
|
||||
|
@ -31,9 +31,9 @@ type PullReq struct {
|
||||
|
||||
ActivitySeq int64 `json:"-"` // not returned, because it's a server's internal field
|
||||
|
||||
MergedBy *int64 `json:"-"` // not returned, because the merger info is in the Merger field
|
||||
Merged *int64 `json:"merged"`
|
||||
MergeStrategy *string `json:"merge_strategy"`
|
||||
MergedBy *int64 `json:"-"` // not returned, because the merger info is in the Merger field
|
||||
Merged *int64 `json:"merged"`
|
||||
MergeStrategy *enum.MergeMethod `json:"merge_strategy"`
|
||||
|
||||
Author PrincipalInfo `json:"author"`
|
||||
Merger *PrincipalInfo `json:"merger"`
|
||||
@ -139,3 +139,7 @@ type PullReqReviewer struct {
|
||||
Reviewer PrincipalInfo `json:"reviewer"`
|
||||
AddedBy PrincipalInfo `json:"added_by"`
|
||||
}
|
||||
|
||||
type MergeResponse struct {
|
||||
SHA string
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user