1//go:build static && system_libgit2
2// +build static,system_libgit2
3
4package commit
5
6import (
7	"context"
8	"encoding/gob"
9	"errors"
10	"fmt"
11	"io"
12
13	git "github.com/libgit2/git2go/v31"
14	"gitlab.com/gitlab-org/gitaly/v14/cmd/gitaly-git2go/git2goutil"
15	"gitlab.com/gitlab-org/gitaly/v14/internal/git2go"
16)
17
18// Run runs the commit subcommand.
19func Run(ctx context.Context, stdin io.Reader, stdout io.Writer) error {
20	var params git2go.CommitParams
21	if err := gob.NewDecoder(stdin).Decode(&params); err != nil {
22		return err
23	}
24
25	commitID, err := commit(ctx, params)
26	return gob.NewEncoder(stdout).Encode(git2go.Result{
27		CommitID: commitID,
28		Error:    git2go.SerializableError(err),
29	})
30}
31
32func commit(ctx context.Context, params git2go.CommitParams) (string, error) {
33	repo, err := git2goutil.OpenRepository(params.Repository)
34	if err != nil {
35		return "", fmt.Errorf("open repository: %w", err)
36	}
37
38	index, err := git.NewIndex()
39	if err != nil {
40		return "", fmt.Errorf("new index: %w", err)
41	}
42
43	var parents []*git.Oid
44	if params.Parent != "" {
45		parentOID, err := git.NewOid(params.Parent)
46		if err != nil {
47			return "", fmt.Errorf("parse base commit oid: %w", err)
48		}
49
50		parents = []*git.Oid{parentOID}
51
52		baseCommit, err := repo.LookupCommit(parentOID)
53		if err != nil {
54			return "", fmt.Errorf("lookup commit: %w", err)
55		}
56
57		baseTree, err := baseCommit.Tree()
58		if err != nil {
59			return "", fmt.Errorf("lookup tree: %w", err)
60		}
61
62		if err := index.ReadTree(baseTree); err != nil {
63			return "", fmt.Errorf("read tree: %w", err)
64		}
65	}
66
67	for _, action := range params.Actions {
68		if err := apply(action, repo, index); err != nil {
69			if git.IsErrorClass(err, git.ErrClassIndex) {
70				err = git2go.IndexError(err.Error())
71			}
72
73			return "", fmt.Errorf("apply action %T: %w", action, err)
74		}
75	}
76
77	treeOID, err := index.WriteTreeTo(repo)
78	if err != nil {
79		return "", fmt.Errorf("write tree: %w", err)
80	}
81
82	author := git.Signature(params.Author)
83	committer := git.Signature(params.Committer)
84	commitID, err := repo.CreateCommitFromIds("", &author, &committer, params.Message, treeOID, parents...)
85	if err != nil {
86		if git.IsErrorClass(err, git.ErrClassInvalid) {
87			return "", git2go.InvalidArgumentError(err.Error())
88		}
89
90		return "", fmt.Errorf("create commit: %w", err)
91	}
92
93	return commitID.String(), nil
94}
95
96func apply(action git2go.Action, repo *git.Repository, index *git.Index) error {
97	switch action := action.(type) {
98	case git2go.ChangeFileMode:
99		return applyChangeFileMode(action, index)
100	case git2go.CreateDirectory:
101		return applyCreateDirectory(action, repo, index)
102	case git2go.CreateFile:
103		return applyCreateFile(action, index)
104	case git2go.DeleteFile:
105		return applyDeleteFile(action, index)
106	case git2go.MoveFile:
107		return applyMoveFile(action, index)
108	case git2go.UpdateFile:
109		return applyUpdateFile(action, index)
110	default:
111		return errors.New("unsupported action")
112	}
113}
114