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
|
package git2go
import (
"bytes"
"context"
"encoding/gob"
"errors"
"fmt"
"reflect"
"gitlab.com/gitlab-org/gitaly/internal/git"
"gitlab.com/gitlab-org/gitaly/internal/gitaly/config"
)
func init() {
for typee := range registeredTypes {
gob.Register(typee)
}
}
var registeredTypes = map[interface{}]struct{}{
ChangeFileMode{}: {},
CreateDirectory{}: {},
CreateFile{}: {},
DeleteFile{}: {},
MoveFile{}: {},
UpdateFile{}: {},
wrapError{}: {},
DirectoryExistsError(""): {},
FileExistsError(""): {},
FileNotFoundError(""): {},
InvalidArgumentError(""): {},
HasConflictsError{}: {},
IndexError(""): {},
}
// Result is the serialized result.
type Result struct {
// CommitID is the result of the call.
CommitID string
// Error is set if the call errord.
Error error
}
// wrapError is used to serialize wrapped errors as fmt.wrapError type only has
// private fields and can't be serialized via gob. It's also used to serialize unregistered
// error types by serializing only their error message.
type wrapError struct {
Message string
Err error
}
func (err wrapError) Error() string { return err.Message }
func (err wrapError) Unwrap() error { return err.Err }
// HasConflictsError is used when a change, for example a revert, could not be
// applied due to a conflict.
type HasConflictsError struct{}
func (err HasConflictsError) Error() string {
return "could not apply due to conflicts"
}
// SerializableError returns an error that is Gob serializable.
// Registered types are serialized directly. Unregistered types
// are transformed in to an opaque error using their error message.
// Wrapped errors remain unwrappable.
func SerializableError(err error) error {
if err == nil {
return nil
}
if unwrappedErr := errors.Unwrap(err); unwrappedErr != nil {
return wrapError{
Message: err.Error(),
Err: SerializableError(unwrappedErr),
}
}
if _, ok := registeredTypes[reflect.Zero(reflect.TypeOf(err)).Interface()]; !ok {
return wrapError{Message: err.Error()}
}
return err
}
// runWithGob runs the specified gitaly-git2go cmd with the request gob-encoded
// as input and returns the commit ID as string or an error.
func runWithGob(ctx context.Context, cfg config.Cfg, cmd string, request interface{}) (git.ObjectID, error) {
input := &bytes.Buffer{}
if err := gob.NewEncoder(input).Encode(request); err != nil {
return "", fmt.Errorf("%s: %w", cmd, err)
}
output, err := run(ctx, binaryPathFromCfg(cfg), input, cmd)
if err != nil {
return "", fmt.Errorf("%s: %w", cmd, err)
}
var result Result
if err := gob.NewDecoder(output).Decode(&result); err != nil {
return "", fmt.Errorf("%s: %w", cmd, err)
}
if result.Error != nil {
return "", fmt.Errorf("%s: %w", cmd, result.Error)
}
commitID, err := git.NewObjectIDFromHex(result.CommitID)
if err != nil {
return "", fmt.Errorf("could not parse commit ID: %w", err)
}
return commitID, nil
}
|