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:
authorPatrick Steinhardt <psteinhardt@gitlab.com>2020-10-12 11:16:50 +0300
committerPatrick Steinhardt <psteinhardt@gitlab.com>2020-10-12 11:16:50 +0300
commit5ad541682a84f8fb9561f28c37f29f3f5a764c35 (patch)
tree5af04767a28e3702cb25c90d01c74787153cd936
parent99c7de6455d846a5fb98622b23afe151abc50bac (diff)
parent6d9126524f23fe27fafc1d9a5c0b24a6f4a97de5 (diff)
Merge branch 'pks-git2go-merge-conflicts' into 'master'
git2go: Make merge conflicts available See merge request gitlab-org/gitaly!2587
-rw-r--r--changelogs/unreleased/pks-git2go-merge-conflicts.yml5
-rw-r--r--cmd/gitaly-git2go/conflicts.go123
-rw-r--r--cmd/gitaly-git2go/conflicts_test.go185
-rw-r--r--cmd/gitaly-git2go/main.go3
-rw-r--r--cmd/gitaly-git2go/merge.go25
-rw-r--r--cmd/gitaly-git2go/util.go28
-rw-r--r--internal/git2go/command.go56
-rw-r--r--internal/git2go/command_test.go28
-rw-r--r--internal/git2go/conflicts.go94
-rw-r--r--internal/git2go/conflicts_test.go128
-rw-r--r--internal/git2go/merge.go47
-rw-r--r--internal/git2go/merge_test.go6
12 files changed, 659 insertions, 69 deletions
diff --git a/changelogs/unreleased/pks-git2go-merge-conflicts.yml b/changelogs/unreleased/pks-git2go-merge-conflicts.yml
new file mode 100644
index 000000000..56f72c881
--- /dev/null
+++ b/changelogs/unreleased/pks-git2go-merge-conflicts.yml
@@ -0,0 +1,5 @@
+---
+title: 'git2go: Implement new command to list merge conflicts'
+merge_request: 2587
+author:
+type: added
diff --git a/cmd/gitaly-git2go/conflicts.go b/cmd/gitaly-git2go/conflicts.go
new file mode 100644
index 000000000..2a96c34a7
--- /dev/null
+++ b/cmd/gitaly-git2go/conflicts.go
@@ -0,0 +1,123 @@
+// +build static,system_libgit2
+
+package main
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "os"
+
+ git "github.com/libgit2/git2go/v30"
+ "gitlab.com/gitlab-org/gitaly/internal/git2go"
+)
+
+type conflictsSubcommand struct {
+ request string
+}
+
+func (cmd *conflictsSubcommand) Flags() *flag.FlagSet {
+ flags := flag.NewFlagSet("conflicts", flag.ExitOnError)
+ flags.StringVar(&cmd.request, "request", "", "git2go.ConflictsCommand")
+ return flags
+}
+
+func indexEntryPath(entry *git.IndexEntry) string {
+ if entry == nil {
+ return ""
+ }
+ return entry.Path
+}
+
+func conflictContent(repo *git.Repository, conflict git.IndexConflict) (string, error) {
+ var ancestor, our, their git.MergeFileInput
+
+ for entry, input := range map[*git.IndexEntry]*git.MergeFileInput{
+ conflict.Ancestor: &ancestor,
+ conflict.Our: &our,
+ conflict.Their: &their,
+ } {
+ if entry == nil {
+ continue
+ }
+
+ blob, err := repo.LookupBlob(entry.Id)
+ if err != nil {
+ return "", fmt.Errorf("could not get conflicting blob: %w", err)
+ }
+
+ input.Path = entry.Path
+ input.Mode = uint(entry.Mode)
+ input.Contents = blob.Contents()
+ }
+
+ merge, err := git.MergeFile(ancestor, our, their, nil)
+ if err != nil {
+ return "", fmt.Errorf("could not compute conflicts: %w", err)
+ }
+
+ return string(merge.Contents), nil
+}
+
+// Run performs a merge and prints resulting conflicts to stdout.
+func (cmd *conflictsSubcommand) Run() error {
+ request, err := git2go.ConflictsCommandFromSerialized(cmd.request)
+ if err != nil {
+ return err
+ }
+
+ repo, err := git.OpenRepository(request.Repository)
+ if err != nil {
+ return fmt.Errorf("could not open repository: %w", err)
+ }
+
+ ours, err := lookupCommit(repo, request.Ours)
+ if err != nil {
+ return fmt.Errorf("could not lookup commit %q: %w", request.Ours, err)
+ }
+
+ theirs, err := lookupCommit(repo, request.Theirs)
+ if err != nil {
+ return fmt.Errorf("could not lookup commit %q: %w", request.Theirs, err)
+ }
+
+ index, err := repo.MergeCommits(ours, theirs, nil)
+ if err != nil {
+ return fmt.Errorf("could not merge commits: %w", err)
+ }
+
+ conflicts, err := index.ConflictIterator()
+ if err != nil {
+ return fmt.Errorf("could not get conflicts: %w", err)
+ }
+
+ var result git2go.ConflictsResult
+ for {
+ conflict, err := conflicts.Next()
+ if err != nil {
+ var gitError git.GitError
+ if errors.As(err, &gitError) && gitError.Code != git.ErrIterOver {
+ return err
+ }
+ break
+ }
+
+ content, err := conflictContent(repo, conflict)
+ if err != nil {
+ return err
+ }
+
+ result.Conflicts = append(result.Conflicts, git2go.Conflict{
+ AncestorPath: indexEntryPath(conflict.Ancestor),
+ OurPath: indexEntryPath(conflict.Our),
+ TheirPath: indexEntryPath(conflict.Their),
+ Content: content,
+ })
+ }
+
+ if err := result.SerializeTo(os.Stdout); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/cmd/gitaly-git2go/conflicts_test.go b/cmd/gitaly-git2go/conflicts_test.go
new file mode 100644
index 000000000..e1310bc1e
--- /dev/null
+++ b/cmd/gitaly-git2go/conflicts_test.go
@@ -0,0 +1,185 @@
+// +build static,system_libgit2
+
+package main
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/gitaly/internal/git2go"
+ "gitlab.com/gitlab-org/gitaly/internal/gitaly/config"
+ "gitlab.com/gitlab-org/gitaly/internal/testhelper"
+)
+
+func TestConflicts(t *testing.T) {
+ testcases := []struct {
+ desc string
+ base map[string]string
+ ours map[string]string
+ theirs map[string]string
+ conflicts []git2go.Conflict
+ }{
+ {
+ desc: "no conflicts",
+ base: map[string]string{
+ "file": "a",
+ },
+ ours: map[string]string{
+ "file": "a",
+ },
+ theirs: map[string]string{
+ "file": "b",
+ },
+ conflicts: nil,
+ },
+ {
+ desc: "single file",
+ base: map[string]string{
+ "file": "a",
+ },
+ ours: map[string]string{
+ "file": "b",
+ },
+ theirs: map[string]string{
+ "file": "c",
+ },
+ conflicts: []git2go.Conflict{
+ {
+ AncestorPath: "file",
+ OurPath: "file",
+ TheirPath: "file",
+ Content: "<<<<<<< file\nb\n=======\nc\n>>>>>>> file\n",
+ },
+ },
+ },
+ {
+ desc: "multiple files with single conflict",
+ base: map[string]string{
+ "file-1": "a",
+ "file-2": "a",
+ },
+ ours: map[string]string{
+ "file-1": "b",
+ "file-2": "b",
+ },
+ theirs: map[string]string{
+ "file-1": "a",
+ "file-2": "c",
+ },
+ conflicts: []git2go.Conflict{
+ {
+ AncestorPath: "file-2",
+ OurPath: "file-2",
+ TheirPath: "file-2",
+ Content: "<<<<<<< file-2\nb\n=======\nc\n>>>>>>> file-2\n",
+ },
+ },
+ },
+ {
+ desc: "multiple conflicts",
+ base: map[string]string{
+ "file-1": "a",
+ "file-2": "a",
+ },
+ ours: map[string]string{
+ "file-1": "b",
+ "file-2": "b",
+ },
+ theirs: map[string]string{
+ "file-1": "c",
+ "file-2": "c",
+ },
+ conflicts: []git2go.Conflict{
+ {
+ AncestorPath: "file-1",
+ OurPath: "file-1",
+ TheirPath: "file-1",
+ Content: "<<<<<<< file-1\nb\n=======\nc\n>>>>>>> file-1\n",
+ },
+ {
+ AncestorPath: "file-2",
+ OurPath: "file-2",
+ TheirPath: "file-2",
+ Content: "<<<<<<< file-2\nb\n=======\nc\n>>>>>>> file-2\n",
+ },
+ },
+ },
+ {
+ desc: "modified-delete-conflict",
+ base: map[string]string{
+ "file": "content",
+ },
+ ours: map[string]string{
+ "file": "changed",
+ },
+ theirs: map[string]string{
+ "different-file": "unrelated",
+ },
+ conflicts: []git2go.Conflict{
+ {
+ AncestorPath: "file",
+ OurPath: "file",
+ TheirPath: "",
+ Content: "<<<<<<< file\nchanged\n=======\n>>>>>>> \n",
+ },
+ },
+ },
+ {
+ // Ruby code doesn't call `merge_commits` with rename
+ // detection and so don't we. The rename conflict is
+ // thus split up into three conflicts.
+ desc: "rename-rename-conflict",
+ base: map[string]string{
+ "file": "a\nb\nc\nd\ne\nf\ng\n",
+ },
+ ours: map[string]string{
+ "renamed-1": "a\nb\nc\nd\ne\nf\ng\n",
+ },
+ theirs: map[string]string{
+ "renamed-2": "a\nb\nc\nd\ne\nf\ng\n",
+ },
+ conflicts: []git2go.Conflict{
+ {
+ AncestorPath: "file",
+ OurPath: "",
+ TheirPath: "",
+ },
+ {
+ AncestorPath: "",
+ OurPath: "renamed-1",
+ TheirPath: "",
+ Content: "a\nb\nc\nd\ne\nf\ng\n",
+ },
+ {
+ AncestorPath: "",
+ OurPath: "",
+ TheirPath: "renamed-2",
+ Content: "a\nb\nc\nd\ne\nf\ng\n",
+ },
+ },
+ },
+ }
+
+ for _, tc := range testcases {
+ _, repoPath, cleanup := testhelper.NewTestRepo(t)
+ defer cleanup()
+
+ base := buildCommit(t, repoPath, nil, tc.base)
+ ours := buildCommit(t, repoPath, base, tc.ours)
+ theirs := buildCommit(t, repoPath, base, tc.theirs)
+
+ t.Run(tc.desc, func(t *testing.T) {
+ ctx, cancel := testhelper.Context()
+ defer cancel()
+
+ response, err := git2go.ConflictsCommand{
+ Repository: repoPath,
+ Ours: ours.String(),
+ Theirs: theirs.String(),
+ }.Run(ctx, config.Config)
+
+ require.NoError(t, err)
+ require.Equal(t, tc.conflicts, response.Conflicts)
+ })
+ }
+}
diff --git a/cmd/gitaly-git2go/main.go b/cmd/gitaly-git2go/main.go
index cdd168482..2d2d1626a 100644
--- a/cmd/gitaly-git2go/main.go
+++ b/cmd/gitaly-git2go/main.go
@@ -14,7 +14,8 @@ type subcmd interface {
}
var subcommands = map[string]subcmd{
- "merge": &mergeSubcommand{},
+ "conflicts": &conflictsSubcommand{},
+ "merge": &mergeSubcommand{},
}
const programName = "gitaly-git2go"
diff --git a/cmd/gitaly-git2go/merge.go b/cmd/gitaly-git2go/merge.go
index 3f07a51ab..97ea6f49a 100644
--- a/cmd/gitaly-git2go/merge.go
+++ b/cmd/gitaly-git2go/merge.go
@@ -6,6 +6,7 @@ import (
"errors"
"flag"
"fmt"
+ "os"
"strings"
"time"
@@ -23,25 +24,6 @@ func (cmd *mergeSubcommand) Flags() *flag.FlagSet {
return flags
}
-func lookupCommit(repo *git.Repository, ref string) (*git.Commit, error) {
- object, err := repo.RevparseSingle(ref)
- if err != nil {
- return nil, fmt.Errorf("could not lookup reference: %w", err)
- }
-
- peeled, err := object.Peel(git.ObjectCommit)
- if err != nil {
- return nil, fmt.Errorf("could not peel reference: %w", err)
- }
-
- commit, err := peeled.AsCommit()
- if err != nil {
- return nil, fmt.Errorf("could not cast to commit: %w", err)
- }
-
- return commit, nil
-}
-
func sanitizeSignatureInfo(info string) string {
return strings.Map(func(r rune) rune {
switch r {
@@ -107,12 +89,9 @@ func (cmd *mergeSubcommand) Run() error {
CommitID: commit.String(),
}
- serialized, err := response.Serialize()
- if err != nil {
+ if err := response.SerializeTo(os.Stdout); err != nil {
return err
}
- fmt.Println(serialized)
-
return nil
}
diff --git a/cmd/gitaly-git2go/util.go b/cmd/gitaly-git2go/util.go
new file mode 100644
index 000000000..d580582cd
--- /dev/null
+++ b/cmd/gitaly-git2go/util.go
@@ -0,0 +1,28 @@
+// +build static,system_libgit2
+
+package main
+
+import (
+ "fmt"
+
+ git "github.com/libgit2/git2go/v30"
+)
+
+func lookupCommit(repo *git.Repository, ref string) (*git.Commit, error) {
+ object, err := repo.RevparseSingle(ref)
+ if err != nil {
+ return nil, fmt.Errorf("could not lookup reference: %w", err)
+ }
+
+ peeled, err := object.Peel(git.ObjectCommit)
+ if err != nil {
+ return nil, fmt.Errorf("could not peel reference: %w", err)
+ }
+
+ commit, err := peeled.AsCommit()
+ if err != nil {
+ return nil, fmt.Errorf("could not cast to commit: %w", err)
+ }
+
+ return commit, nil
+}
diff --git a/internal/git2go/command.go b/internal/git2go/command.go
new file mode 100644
index 000000000..2409b847c
--- /dev/null
+++ b/internal/git2go/command.go
@@ -0,0 +1,56 @@
+package git2go
+
+import (
+ "bytes"
+ "context"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os/exec"
+ "path"
+ "strings"
+
+ "gitlab.com/gitlab-org/gitaly/internal/command"
+ "gitlab.com/gitlab-org/gitaly/internal/gitaly/config"
+)
+
+func run(ctx context.Context, cfg config.Cfg, subcommand string, arg string) (string, error) {
+ binary := path.Join(cfg.BinDir, "gitaly-git2go")
+
+ var stderr, stdout bytes.Buffer
+ cmd, err := command.New(ctx, exec.Command(binary, subcommand, "-request", arg), nil, &stdout, &stderr)
+ if err != nil {
+ return "", err
+ }
+
+ if err := cmd.Wait(); err != nil {
+ if _, ok := err.(*exec.ExitError); ok {
+ return "", fmt.Errorf("%s", stderr.String())
+ }
+ return "", err
+ }
+
+ return stdout.String(), nil
+}
+
+func serialize(v interface{}) (string, error) {
+ marshalled, err := json.Marshal(v)
+ if err != nil {
+ return "", err
+ }
+ return base64.StdEncoding.EncodeToString(marshalled), nil
+}
+
+func deserialize(serialized string, v interface{}) error {
+ base64Decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(serialized))
+ jsonDecoder := json.NewDecoder(base64Decoder)
+ return jsonDecoder.Decode(v)
+}
+
+func serializeTo(writer io.Writer, v interface{}) error {
+ base64Encoder := base64.NewEncoder(base64.StdEncoding, writer)
+ defer base64Encoder.Close()
+ jsonEncoder := json.NewEncoder(base64Encoder)
+ return jsonEncoder.Encode(v)
+}
diff --git a/internal/git2go/command_test.go b/internal/git2go/command_test.go
new file mode 100644
index 000000000..14f1c0a3a
--- /dev/null
+++ b/internal/git2go/command_test.go
@@ -0,0 +1,28 @@
+package git2go
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestSerialization_SerializeTo(t *testing.T) {
+ type testStruct struct {
+ Contents string `json:"contents"`
+ }
+
+ var buf bytes.Buffer
+
+ input := testStruct{
+ Contents: "foobar",
+ }
+ err := serializeTo(&buf, &input)
+ require.NoError(t, err)
+ require.NotZero(t, buf.Len())
+
+ var output testStruct
+ err = deserialize(buf.String(), &output)
+ require.NoError(t, err)
+ require.Equal(t, input, output)
+}
diff --git a/internal/git2go/conflicts.go b/internal/git2go/conflicts.go
new file mode 100644
index 000000000..101c260ae
--- /dev/null
+++ b/internal/git2go/conflicts.go
@@ -0,0 +1,94 @@
+package git2go
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+
+ "gitlab.com/gitlab-org/gitaly/internal/gitaly/config"
+)
+
+// ConflictsCommand contains parameters to perform a merge and return its conflicts.
+type ConflictsCommand struct {
+ // Repository is the path to execute merge in.
+ Repository string `json:"repository"`
+ // Ours is the commit that is to be merged into theirs.
+ Ours string `json:"ours"`
+ // Theirs is the commit into which ours is to be merged.
+ Theirs string `json:"theirs"`
+}
+
+// Conflict represents a merge conflict for a single file.
+type Conflict struct {
+ // AncestorPath is the path of the ancestor.
+ AncestorPath string `json:"ancestor_path"`
+ // OurPath is the path of ours.
+ OurPath string `json:"our_path"`
+ // TheirPath is the path of theirs.
+ TheirPath string `json:"their_path"`
+ // Content contains the conflicting merge results.
+ Content string `json:"content"`
+}
+
+// ConflictsResult contains all conflicts resulting from a merge.
+type ConflictsResult struct {
+ // Conflicts
+ Conflicts []Conflict `json:"conflicts"`
+}
+
+// ConflictsCommandFromSerialized constructs a ConflictsCommand from its serialized representation.
+func ConflictsCommandFromSerialized(serialized string) (ConflictsCommand, error) {
+ var request ConflictsCommand
+ if err := deserialize(serialized, &request); err != nil {
+ return ConflictsCommand{}, err
+ }
+
+ if err := request.verify(); err != nil {
+ return ConflictsCommand{}, fmt.Errorf("conflicts: %w: %s", ErrInvalidArgument, err.Error())
+ }
+
+ return request, nil
+}
+
+// Serialize serializes the conflicts result.
+func (m ConflictsResult) SerializeTo(writer io.Writer) error {
+ return serializeTo(writer, m)
+}
+
+// Run performs a merge via gitaly-git2go and returns all resulting conflicts.
+func (c ConflictsCommand) Run(ctx context.Context, cfg config.Cfg) (ConflictsResult, error) {
+ if err := c.verify(); err != nil {
+ return ConflictsResult{}, fmt.Errorf("conflicts: %w: %s", ErrInvalidArgument, err.Error())
+ }
+
+ serialized, err := serialize(c)
+ if err != nil {
+ return ConflictsResult{}, err
+ }
+
+ stdout, err := run(ctx, cfg, "conflicts", serialized)
+ if err != nil {
+ return ConflictsResult{}, err
+ }
+
+ var response ConflictsResult
+ if err := deserialize(stdout, &response); err != nil {
+ return ConflictsResult{}, err
+ }
+
+ return response, nil
+}
+
+func (c ConflictsCommand) verify() error {
+ if c.Repository == "" {
+ return errors.New("missing repository")
+ }
+ if c.Ours == "" {
+ return errors.New("missing ours")
+ }
+ if c.Theirs == "" {
+ return errors.New("missing theirs")
+ }
+ return nil
+}
diff --git a/internal/git2go/conflicts_test.go b/internal/git2go/conflicts_test.go
new file mode 100644
index 000000000..6aeeb2903
--- /dev/null
+++ b/internal/git2go/conflicts_test.go
@@ -0,0 +1,128 @@
+package git2go
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestConflictsCommand_Serialization(t *testing.T) {
+ testcases := []struct {
+ desc string
+ cmd ConflictsCommand
+ err string
+ }{
+ {
+ desc: "missing repository",
+ cmd: ConflictsCommand{},
+ err: "missing repository",
+ },
+ {
+ desc: "missing theirs",
+ cmd: ConflictsCommand{
+ Repository: "foo",
+ Ours: "refs/heads/master",
+ },
+ err: "missing theirs",
+ },
+ {
+ desc: "missing ours",
+ cmd: ConflictsCommand{
+ Repository: "foo",
+ Theirs: "refs/heads/master",
+ },
+ err: "missing ours",
+ },
+ {
+ desc: "valid command",
+ cmd: ConflictsCommand{
+ Repository: "foo",
+ Ours: "refs/heads/master",
+ Theirs: "refs/heads/foo",
+ },
+ },
+ }
+
+ for _, tc := range testcases {
+ t.Run(tc.desc, func(t *testing.T) {
+ serialized, err := serialize(tc.cmd)
+ require.NoError(t, err)
+
+ deserialized, err := ConflictsCommandFromSerialized(serialized)
+
+ if tc.err != "" {
+ require.Error(t, err)
+ require.Contains(t, err.Error(), tc.err)
+ } else {
+ require.NoError(t, err)
+ require.Equal(t, deserialized, tc.cmd)
+ }
+ })
+ }
+}
+
+func TestConflictsResult_Serialization(t *testing.T) {
+ serializeResult := func(t *testing.T, result ConflictsResult) string {
+ var buf bytes.Buffer
+ err := result.SerializeTo(&buf)
+ require.NoError(t, err)
+ return buf.String()
+ }
+
+ testcases := []struct {
+ desc string
+ serialized string
+ expected ConflictsResult
+ err string
+ }{
+ {
+ desc: "empty merge result",
+ serialized: serializeResult(t, ConflictsResult{}),
+ expected: ConflictsResult{},
+ },
+ {
+ desc: "merge result with commit",
+ serialized: serializeResult(t, ConflictsResult{
+ Conflicts: []Conflict{
+ {
+ AncestorPath: "dir/ancestor",
+ OurPath: "dir/our",
+ TheirPath: "dir/their",
+ Content: "content",
+ },
+ },
+ }),
+ expected: ConflictsResult{
+ Conflicts: []Conflict{
+ {
+ AncestorPath: "dir/ancestor",
+ OurPath: "dir/our",
+ TheirPath: "dir/their",
+ Content: "content",
+ },
+ },
+ },
+ },
+ {
+ desc: "invalid serialized representation",
+ serialized: "xvlc",
+ err: "invalid character",
+ },
+ }
+
+ for _, tc := range testcases {
+ t.Run(tc.desc, func(t *testing.T) {
+ var deserialized ConflictsResult
+ err := deserialize(tc.serialized, &deserialized)
+
+ if tc.err != "" {
+ require.Error(t, err)
+ require.Contains(t, err.Error(), tc.err)
+ } else {
+ require.NoError(t, err)
+ require.Equal(t, deserialized, tc.expected)
+ }
+ })
+ }
+}
diff --git a/internal/git2go/merge.go b/internal/git2go/merge.go
index b901353af..a60ff51c8 100644
--- a/internal/git2go/merge.go
+++ b/internal/git2go/merge.go
@@ -1,18 +1,12 @@
package git2go
import (
- "bytes"
"context"
- "encoding/base64"
- "encoding/json"
"errors"
"fmt"
- "os/exec"
- "path"
- "strings"
+ "io"
"time"
- "gitlab.com/gitlab-org/gitaly/internal/command"
"gitlab.com/gitlab-org/gitaly/internal/gitaly/config"
)
@@ -45,20 +39,6 @@ type MergeResult struct {
CommitID string `json:"commit_id"`
}
-func serialize(v interface{}) (string, error) {
- marshalled, err := json.Marshal(v)
- if err != nil {
- return "", err
- }
- return base64.StdEncoding.EncodeToString(marshalled), nil
-}
-
-func deserialize(serialized string, v interface{}) error {
- base64Decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(serialized))
- jsonDecoder := json.NewDecoder(base64Decoder)
- return jsonDecoder.Decode(v)
-}
-
// MergeCommandFromSerialized deserializes the merge request from its JSON representation encoded with base64.
func MergeCommandFromSerialized(serialized string) (MergeCommand, error) {
var request MergeCommand
@@ -73,9 +53,9 @@ func MergeCommandFromSerialized(serialized string) (MergeCommand, error) {
return request, nil
}
-// Serialize serializes the merge response into its JSON representation and encodes it with base64.
-func (m MergeResult) Serialize() (string, error) {
- return serialize(m)
+// SerializeTo serializes the merge result and writes it into the writer.
+func (m MergeResult) SerializeTo(w io.Writer) error {
+ return serializeTo(w, m)
}
// Merge performs a merge via gitaly-git2go.
@@ -123,22 +103,3 @@ func (m MergeCommand) verify() error {
}
return nil
}
-
-func run(ctx context.Context, cfg config.Cfg, subcommand string, arg string) (string, error) {
- binary := path.Join(cfg.BinDir, "gitaly-git2go")
-
- var stderr, stdout bytes.Buffer
- cmd, err := command.New(ctx, exec.Command(binary, subcommand, "-request", arg), nil, &stdout, &stderr)
- if err != nil {
- return "", err
- }
-
- if err := cmd.Wait(); err != nil {
- if _, ok := err.(*exec.ExitError); ok {
- return "", fmt.Errorf("%s", stderr.String())
- }
- return "", err
- }
-
- return stdout.String(), nil
-}
diff --git a/internal/git2go/merge_test.go b/internal/git2go/merge_test.go
index 1de4f3cab..5f0bc60c4 100644
--- a/internal/git2go/merge_test.go
+++ b/internal/git2go/merge_test.go
@@ -1,6 +1,7 @@
package git2go
import (
+ "bytes"
"testing"
"time"
@@ -108,9 +109,10 @@ func TestGit2Go_MergeCommandSerialization(t *testing.T) {
func TestGit2Go_MergeResultSerialization(t *testing.T) {
serializeResult := func(t *testing.T, result MergeResult) string {
- serialized, err := result.Serialize()
+ var buf bytes.Buffer
+ err := result.SerializeTo(&buf)
require.NoError(t, err)
- return serialized
+ return buf.String()
}
testcases := []struct {