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 streams object headers and bytes; eligibility, deduplication and byte limits are decided by domain.CommitObjectBudget, request validation by domain.ValidateCommitObjects
23func (g *Git) ReadCommitObjects(ctx context.Context, repo domain.RepoName, ids []domain.ObjectID) (map[domain.ObjectID][]byte, error) {
24 if err := domain.ValidateCommitObjects(ids, ports.MaxCommitLimit); err != nil {
25 return nil, err
26 }
27 dir, err := g.repoDir(repo)
28 if err != nil {
29 return nil, err
30 }
31 out := make(map[domain.ObjectID][]byte, len(ids))
32 err = g.cat.do(ctx, dir, func(c *catConn) error {
33 clear(out) // a retry on a fresh process starts over
34 var budget domain.CommitObjectBudget
35 for _, id := range ids {
36 if err := ctx.Err(); err != nil {
37 return err
38 }
39 if budget.Seen(id) {
40 continue
41 }
42 h, err := c.info(id.String())
43 if notFound(err) {
44 continue
45 }
46 if err != nil {
47 return err
48 }
49 take, stop := budget.Admit(id, h.Type == "commit", h.Size)
50 if stop {
51 break
52 }
53 if !take {
54 continue
55 }
56 _, data, err := c.contents(id.String(), domain.MaxCommitObjectBytes)
57 if err != nil {
58 return err
59 }
60 out[id] = data
61 budget.Charge(id, int64(len(data)))
62 }
63 return nil
64 })
65 if err != nil {
66 return nil, fmt.Errorf("read commit objects of %q: %w", repo, err)
67 }
68 return out, nil
69}