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

merge.go « git2go « internal - gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: b901353af378abc4c5571ce61a62255b76c00fdf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package git2go

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"os/exec"
	"path"
	"strings"
	"time"

	"gitlab.com/gitlab-org/gitaly/internal/command"
	"gitlab.com/gitlab-org/gitaly/internal/gitaly/config"
)

var (
	// ErrInvalidArgument is returned in case the merge arguments are invalid.
	ErrInvalidArgument = errors.New("invalid parameters")
)

// MergeCommand contains parameters to perform a merge.
type MergeCommand struct {
	// Repository is the path to execute merge in.
	Repository string `json:"repository"`
	// AuthorName is the author name of merge commit.
	AuthorName string `json:"author_name"`
	// AuthorMail is the author mail of merge commit.
	AuthorMail string `json:"author_mail"`
	// AuthorDate is the auithor date of merge commit.
	AuthorDate time.Time `json:"author_date"`
	// Message is the message to be used for the merge commit.
	Message string `json:"message"`
	// 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"`
}

// MergeResult contains results from a merge.
type MergeResult struct {
	// CommitID is the object ID of the generated merge commit.
	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
	if err := deserialize(serialized, &request); err != nil {
		return MergeCommand{}, err
	}

	if err := request.verify(); err != nil {
		return MergeCommand{}, fmt.Errorf("merge: %w: %s", ErrInvalidArgument, err.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)
}

// Merge performs a merge via gitaly-git2go.
func (m MergeCommand) Run(ctx context.Context, cfg config.Cfg) (MergeResult, error) {
	if err := m.verify(); err != nil {
		return MergeResult{}, fmt.Errorf("merge: %w: %s", ErrInvalidArgument, err.Error())
	}

	serialized, err := serialize(m)
	if err != nil {
		return MergeResult{}, err
	}

	stdout, err := run(ctx, cfg, "merge", serialized)
	if err != nil {
		return MergeResult{}, err
	}

	var response MergeResult
	if err := deserialize(stdout, &response); err != nil {
		return MergeResult{}, err
	}

	return response, nil
}

func (m MergeCommand) verify() error {
	if m.Repository == "" {
		return errors.New("missing repository")
	}
	if m.AuthorName == "" {
		return errors.New("missing author name")
	}
	if m.AuthorMail == "" {
		return errors.New("missing author mail")
	}
	if m.Message == "" {
		return errors.New("missing message")
	}
	if m.Ours == "" {
		return errors.New("missing ours")
	}
	if m.Theirs == "" {
		return errors.New("missing theirs")
	}
	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
}