1package git2go 2 3import ( 4 "context" 5 "errors" 6 "fmt" 7 "io" 8 9 "gitlab.com/gitlab-org/gitaly/v14/internal/git/repository" 10 "google.golang.org/grpc/codes" 11 "google.golang.org/grpc/status" 12) 13 14// ConflictsCommand contains parameters to perform a merge and return its conflicts. 15type ConflictsCommand struct { 16 // Repository is the path to execute merge in. 17 Repository string `json:"repository"` 18 // Ours is the commit that is to be merged into theirs. 19 Ours string `json:"ours"` 20 // Theirs is the commit into which ours is to be merged. 21 Theirs string `json:"theirs"` 22} 23 24// ConflictEntry represents a conflict entry which is one of the sides of a conflict. 25type ConflictEntry struct { 26 // Path is the path of the conflicting file. 27 Path string `json:"path"` 28 // Mode is the mode of the conflicting file. 29 Mode int32 `json:"mode"` 30} 31 32// Conflict represents a merge conflict for a single file. 33type Conflict struct { 34 // Ancestor is the conflict entry of the merge-base. 35 Ancestor ConflictEntry `json:"ancestor"` 36 // Our is the conflict entry of ours. 37 Our ConflictEntry `json:"our"` 38 // Their is the conflict entry of theirs. 39 Their ConflictEntry `json:"their"` 40 // Content contains the conflicting merge results. 41 Content []byte `json:"content"` 42} 43 44// ConflictError is an error which happened during conflict resolution. 45type ConflictError struct { 46 // Code is the GRPC error code 47 Code codes.Code 48 // Message is the error message 49 Message string 50} 51 52// ConflictsResult contains all conflicts resulting from a merge. 53type ConflictsResult struct { 54 // Conflicts 55 Conflicts []Conflict `json:"conflicts"` 56 // Error is an optional conflict error 57 Error ConflictError `json:"error"` 58} 59 60// ConflictsCommandFromSerialized constructs a ConflictsCommand from its serialized representation. 61func ConflictsCommandFromSerialized(serialized string) (ConflictsCommand, error) { 62 var request ConflictsCommand 63 if err := deserialize(serialized, &request); err != nil { 64 return ConflictsCommand{}, err 65 } 66 67 if err := request.verify(); err != nil { 68 return ConflictsCommand{}, fmt.Errorf("conflicts: %w: %s", ErrInvalidArgument, err.Error()) 69 } 70 71 return request, nil 72} 73 74// SerializeTo serializes the conflicts result and writes it into the writer. 75func (m ConflictsResult) SerializeTo(writer io.Writer) error { 76 return serializeTo(writer, m) 77} 78 79// Conflicts performs a merge via gitaly-git2go and returns all resulting conflicts. 80func (b Executor) Conflicts(ctx context.Context, repo repository.GitRepo, c ConflictsCommand) (ConflictsResult, error) { 81 if err := c.verify(); err != nil { 82 return ConflictsResult{}, fmt.Errorf("conflicts: %w: %s", ErrInvalidArgument, err.Error()) 83 } 84 85 serialized, err := serialize(c) 86 if err != nil { 87 return ConflictsResult{}, err 88 } 89 90 stdout, err := b.run(ctx, repo, nil, "conflicts", "-request", serialized) 91 if err != nil { 92 return ConflictsResult{}, err 93 } 94 95 var response ConflictsResult 96 if err := deserialize(stdout.String(), &response); err != nil { 97 return ConflictsResult{}, err 98 } 99 100 if response.Error.Code != codes.OK { 101 return ConflictsResult{}, status.Error(response.Error.Code, response.Error.Message) 102 } 103 104 return response, nil 105} 106 107func (c ConflictsCommand) verify() error { 108 if c.Repository == "" { 109 return errors.New("missing repository") 110 } 111 if c.Ours == "" { 112 return errors.New("missing ours") 113 } 114 if c.Theirs == "" { 115 return errors.New("missing theirs") 116 } 117 return nil 118} 119