tinyrelayfile
tinyrelay/tinygit/collaboration_diff.go/

tinygit/collaboration_diff.go raw
1package tinygit
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "strings"
8 "time"
9)
10
11const collaborationDiffBytes = 512 * 1024
12const collaborationDiffTimeout = 5 * time.Second
13
14// CollaborationDiff is the result of rendering a pull request's local Git
15// objects. Status is "ok" when Diff is authoritative and
16// "diff_unavailable" when one or both commits are not present locally.
17type CollaborationDiff struct {
18 Status string `json:"status"`
19 Base string `json:"base,omitempty"`
20 Tip string `json:"tip,omitempty"`
21 MergeBase string `json:"merge_base,omitempty"`
22 Diff string `json:"diff,omitempty"`
23 Truncated bool `json:"truncated,omitempty"`
24 Reason string `json:"reason,omitempty"`
25}
26
27// PRDiff renders a bounded diff from commits already present in the
28// repository. It never fetches or resolves a remote ref. A supplied base is
29// used only after it is verified as a local commit; when it is empty, Git's
30// merge-base with the repository target is used.
31func (g *GitRelay) PRDiff(ctx context.Context, r Repository, base, tip string) CollaborationDiff {
32 ctx, cancel := context.WithTimeout(ctx, collaborationDiffTimeout)
33 defer cancel()
34 result := CollaborationDiff{Status: "diff_unavailable", Base: base, Tip: tip}
35 if !isObjectID(base) && base != "" {
36 result.Reason = "invalid base commit"
37 return result
38 }
39 if !isObjectID(tip) {
40 result.Reason = "invalid tip commit"
41 return result
42 }
43 if err := g.localCommit(ctx, r, tip); err != nil {
44 result.Reason = "tip commit is unavailable locally"
45 return result
46 }
47 if base != "" {
48 if err := g.localCommit(ctx, r, base); err != nil {
49 result.Reason = "base commit is unavailable locally"
50 return result
51 }
52 }
53 target, err := g.targetCommit(ctx, r)
54 if err != nil {
55 result.Reason = "repository target commit is unavailable locally"
56 return result
57 }
58 if base != "" {
59 // A PR base is meaningful only when it is reachable from both sides of
60 // the comparison. Checking the current target as well prevents an old
61 // or malicious PR-only ancestor from being presented as the repository
62 // diff base.
63 if err := g.browseCommand(ctx, r, "merge-base", "--is-ancestor", base, tip).Run(); err != nil {
64 result.Reason = "base is not an ancestor of tip"
65 return result
66 }
67 if err := g.browseCommand(ctx, r, "merge-base", "--is-ancestor", base, target).Run(); err != nil {
68 result.Reason = "base is not an ancestor of repository target"
69 return result
70 }
71 result.MergeBase = base
72 } else {
73 b, err := g.browseCommand(ctx, r, "merge-base", "--", tip, target).Output()
74 if err != nil || !isObjectID(strings.TrimSpace(string(b))) {
75 result.Reason = "merge base is unavailable locally"
76 return result
77 }
78 result.MergeBase = strings.TrimSpace(string(b))
79 result.Base = result.MergeBase
80 }
81
82 b, truncated, err := readBrowseOutput(g.browseCommand(ctx, r, "diff", "--format=", "--no-ext-diff", "--no-textconv", "--no-renames", "--binary", result.MergeBase, tip, "--"), collaborationDiffBytes)
83 if err != nil {
84 result.Reason = "unable to render local diff"
85 return result
86 }
87 result.Status = "ok"
88 result.Diff = strings.ToValidUTF8(string(b), "�")
89 result.Truncated = truncated
90 return result
91}
92
93func (g *GitRelay) targetCommit(ctx context.Context, r Repository) (string, error) {
94 target := strings.TrimPrefix(r.Head, "ref: ")
95 if target == "" || !validBrowsePath(target) {
96 return "", errors.New("missing repository target")
97 }
98 b, err := g.browseCommand(ctx, r, "rev-parse", "--verify", "--end-of-options", target+"^{commit}").Output()
99 if err != nil {
100 return "", err
101 }
102 target = strings.TrimSpace(string(b))
103 if !isObjectID(target) || g.localCommit(ctx, r, target) != nil {
104 return "", errors.New("target commit unavailable")
105 }
106 return target, nil
107}
108
109func (g *GitRelay) localCommit(ctx context.Context, r Repository, oid string) error {
110 if !isObjectID(oid) {
111 return errors.New("invalid object id")
112 }
113 b, err := g.browseCommand(ctx, r, "cat-file", "-t", oid).Output()
114 if err != nil || strings.TrimSpace(string(b)) != "commit" {
115 return fmt.Errorf("commit unavailable: %s", oid)
116 }
117 return nil
118}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs