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

apply.go « git2go « internal - gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 5c2b70da20b575e25886432ed3c9345b0ff2b8ce (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
package git2go

import (
	"context"
	"encoding/gob"
	"fmt"
	"io"

	"gitlab.com/gitlab-org/gitaly/v15/internal/git"
	"gitlab.com/gitlab-org/gitaly/v15/internal/git/repository"
)

// ErrMergeConflict is returned when there is a merge conflict.
var ErrMergeConflict = wrapError{Message: "merge conflict"}

// Patch represents a single patch.
type Patch struct {
	// Author is the author of the patch.
	Author Signature
	// Message is used as the commit message when applying the patch.
	Message string
	// Diff contains the diff of the patch.
	Diff []byte
}

// ApplyParams are the parameters for Apply.
type ApplyParams struct {
	// Repository is the path to the repository.
	Repository string
	// Committer is the committer applying the patch.
	Committer Signature
	// ParentCommit is the OID of the commit to apply the patches against.
	ParentCommit string
	// Patches iterates over all the patches to be applied.
	Patches PatchIterator
}

// PatchIterator iterates over a stream of patches.
type PatchIterator interface {
	// Next returns whether there is a next patch.
	Next() bool
	// Value returns the patch being currently iterated upon.
	Value() Patch
	// Err returns the iteration error. Err should
	// be always checked after Next returns false.
	Err() error
}

type slicePatchIterator struct {
	value   Patch
	patches []Patch
}

// NewSlicePatchIterator returns a PatchIterator that iterates over the slice
// of patches.
func NewSlicePatchIterator(patches []Patch) PatchIterator {
	return &slicePatchIterator{patches: patches}
}

func (iter *slicePatchIterator) Next() bool {
	if len(iter.patches) == 0 {
		return false
	}

	iter.value = iter.patches[0]
	iter.patches = iter.patches[1:]
	return true
}

func (iter *slicePatchIterator) Value() Patch { return iter.value }

func (iter *slicePatchIterator) Err() error { return nil }

// Apply applies the provided patches and returns the OID of the commit with the patches
// applied.
func (b *Executor) Apply(ctx context.Context, repo repository.GitRepo, params ApplyParams) (git.ObjectID, error) {
	reader, writer := io.Pipe()
	defer writer.Close()

	go func() {
		// CloseWithError is documented to always return nil.
		_ = writer.CloseWithError(func() error {
			patches := params.Patches
			params.Patches = nil

			encoder := gob.NewEncoder(writer)
			if err := encoder.Encode(params); err != nil {
				return fmt.Errorf("encode header: %w", err)
			}

			for patches.Next() {
				if err := encoder.Encode(patches.Value()); err != nil {
					return fmt.Errorf("encode patch: %w", err)
				}
			}

			if err := patches.Err(); err != nil {
				return fmt.Errorf("patch iterator: %w", err)
			}

			return nil
		}())
	}()

	execEnv := b.gitCmdFactory.GetExecutionEnvironment(ctx)

	args := []string{"-git-binary-path", execEnv.BinaryPath}
	if b.signingKey != "" {
		args = append(args, "-signing-key", b.signingKey)
	}

	var result Result
	output, err := b.run(ctx, repo, reader, "apply", args...)
	if err != nil {
		return "", fmt.Errorf("run: %w", err)
	}

	if err := gob.NewDecoder(output).Decode(&result); err != nil {
		return "", fmt.Errorf("decode: %w", err)
	}

	if result.Err != nil {
		return "", result.Err
	}

	commitID, err := git.ObjectHashSHA1.FromHex(result.CommitID)
	if err != nil {
		return "", fmt.Errorf("could not parse commit ID: %w", err)
	}

	return commitID, nil
}