diff options
Diffstat (limited to 'cmd/gitaly-git2go/submodule.go')
-rw-r--r-- | cmd/gitaly-git2go/submodule.go | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/cmd/gitaly-git2go/submodule.go b/cmd/gitaly-git2go/submodule.go new file mode 100644 index 000000000..d256188b1 --- /dev/null +++ b/cmd/gitaly-git2go/submodule.go @@ -0,0 +1,136 @@ +// +build static,system_libgit2 + +package main + +import ( + "context" + "flag" + "fmt" + "io" + "time" + + git "github.com/libgit2/git2go/v30" + "gitlab.com/gitlab-org/gitaly/internal/git2go" +) + +type submoduleSubcommand struct { + request string +} + +func (cmd *submoduleSubcommand) Flags() *flag.FlagSet { + flags := flag.NewFlagSet("submodule", flag.ExitOnError) + flags.StringVar(&cmd.request, "request", "", "git2go.SubmoduleCommand") + return flags +} + +func (cmd *submoduleSubcommand) Run(_ context.Context, _ io.Reader, w io.Writer) error { + request, err := git2go.SubmoduleCommandFromSerialized(cmd.request) + if err != nil { + return fmt.Errorf("deserializing submodule command request: %w", err) + } + + if request.AuthorDate.IsZero() { + request.AuthorDate = time.Now() + } + + smCommitOID, err := git.NewOid(request.CommitSHA) + if err != nil { + return fmt.Errorf("converting %s to OID: %w", request.CommitSHA, err) + } + + repo, err := git.OpenRepository(request.Repository) + if err != nil { + return fmt.Errorf("open repository: %w", err) + } + + fullBranchRefName := "refs/heads/" + request.Branch + o, err := repo.RevparseSingle(fullBranchRefName) + if err != nil { + return fmt.Errorf("%s: %w", git2go.LegacyErrPrefixInvalidBranch, err) //nolint + } + + startCommit, err := o.AsCommit() + if err != nil { + return fmt.Errorf("peeling %s as a commit: %w", o.Id(), err) + } + + rootTree, err := startCommit.Tree() + if err != nil { + return fmt.Errorf("root tree from starting commit: %w", err) + } + + index, err := git.NewIndex() + if err != nil { + return fmt.Errorf("creating new index: %w", err) + } + + if err := index.ReadTree(rootTree); err != nil { + return fmt.Errorf("reading root tree into index: %w", err) + } + + smEntry, err := index.EntryByPath(request.Submodule, 0) + if err != nil { + return fmt.Errorf( + "%s: %w", + git2go.LegacyErrPrefixInvalidSubmodulePath, err, + ) //nolint + } + + if smEntry.Id.Cmp(smCommitOID) == 0 { + //nolint + return fmt.Errorf( + "The submodule %s is already at %s", + request.Submodule, request.CommitSHA, + ) + } + + if smEntry.Mode != git.FilemodeCommit { + return fmt.Errorf( + "%s: %w", + git2go.LegacyErrPrefixInvalidSubmodulePath, err, + ) //nolint + } + + newEntry := *smEntry // copy by value + newEntry.Id = smCommitOID // assign new commit SHA + if err := index.Add(&newEntry); err != nil { + return fmt.Errorf("add new submodule entry to index: %w", err) + } + + newRootTreeOID, err := index.WriteTreeTo(repo) + if err != nil { + return fmt.Errorf("write index to repo: %w", err) + } + + newTree, err := repo.LookupTree(newRootTreeOID) + if err != nil { + return fmt.Errorf("looking up new submodule entry root tree: %w", err) + } + + committer := git.Signature( + git2go.NewSignature( + request.AuthorName, + request.AuthorMail, + request.AuthorDate, + ), + ) + newCommitOID, err := repo.CreateCommit( + "", // caller should update branch with hooks + &committer, + &committer, + request.Message, + newTree, + startCommit, + ) + if err != nil { + // nolint + return fmt.Errorf( + "%s: %w", + git2go.LegacyErrPrefixFailedCommit, err, + ) + } + + return git2go.SubmoduleResult{ + CommitID: newCommitOID.String(), + }.SerializeTo(w) +} |