1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package gitcli
4
5import (
6 "context"
7 "fmt"
8
9 "bnema.dev/ermite/internal/domain"
10 "bnema.dev/ermite/internal/ports"
11)
12
13var _ ports.CommitObjectReader = (*Git)(nil)
14
15// ReadCommitObjects reads the raw content of commits with the cat-file pool:
16// one `info` per id, then `contents` for the commits of acceptable size. Ids
17// that are missing, are not commits, or are larger than
18// domain.MaxCommitObjectBytes are left out. The read ends at the first commit
19// that would take the total over domain.MaxCommitObjectsBytes (known from
20// `info`, before any content is read, so the stream stays consistent).
21//
22//hexcheck:ignore suspicious-business-logic-in-adapter applies the limits of the port and of the domain
23func (g *Git) ReadCommitObjects(ctx context.Context, repo domain.RepoName, ids []domain.ObjectID) (map[domain.ObjectID][]byte, error) {
24 if len(ids) > ports.MaxCommitLimit {
25 return nil, fmt.Errorf("%w: %d commit objects asked, at most %d", domain.ErrInvalid, len(ids), ports.MaxCommitLimit)
26 }
27 for _, id := range ids {
28 if id.IsZero() {
29 return nil, fmt.Errorf("%w: empty commit id", domain.ErrInvalidObjectID)
30 }
31 }
32 dir, err := g.repoDir(repo)
33 if err != nil {
34 return nil, err
35 }
36 out := make(map[domain.ObjectID][]byte, len(ids))
37 err = g.cat.do(ctx, dir, func(c *catConn) error {
38 clear(out) // a retry on a fresh process starts over
39 var total int64
40 for _, id := range ids {
41 if err := ctx.Err(); err != nil {
42 return err
43 }
44 if _, done := out[id]; done {
45 continue
46 }
47 h, err := c.info(id.String())
48 if notFound(err) {
49 continue
50 }
51 if err != nil {
52 return err
53 }
54 if h.Type != "commit" || h.Size > domain.MaxCommitObjectBytes {
55 continue
56 }
57 if total+h.Size > domain.MaxCommitObjectsBytes {
58 break
59 }
60 _, data, err := c.contents(id.String(), domain.MaxCommitObjectBytes)
61 if err != nil {
62 return err
63 }
64 out[id] = data
65 total += int64(len(data))
66 }
67 return nil
68 })
69 if err != nil {
70 return nil, fmt.Errorf("read commit objects of %q: %w", repo, err)
71 }
72 return out, nil
73}