Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Code

392 commit(s) on main

Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys keenan · 1h ago 4b8c9e1d
history blame raw 3.9 KiB
1package client
2
3import (
4 "fmt"
5 "strings"
6
7 "rodent.software/vole/internal/format"
8 "rodent.software/vole/metadata"
9)
10
11// ResolveRequestCommentID resolves a typed comment id against one request's
12// thread, the way issue and comment ids are resolved: a prefix is enough, and an
13// ambiguous one names its matches instead of guessing.
14func ResolveRequestCommentID(b *Branch, requestID, given string) (string, error) {
15 given = strings.ToUpper(strings.TrimSpace(given))
16 if given == "" {
17 return "", fmt.Errorf("a comment id is required")
18 }
19 r, err := metadata.FindRequest(b.Repo, requestID)
20 if err != nil {
21 return "", err
22 }
23 var matches []string
24 for _, cm := range r.Comments {
25 if cm.ID == given {
26 return cm.ID, nil
27 }
28 if strings.HasPrefix(cm.ID, given) {
29 matches = append(matches, cm.ID)
30 }
31 }
32 switch len(matches) {
33 case 1:
34 return matches[0], nil
35 case 0:
36 return "", fmt.Errorf("no comment matching %q on %s", given, requestID)
37 default:
38 return "", fmt.Errorf("%q matches %d comments: %s", given, len(matches), strings.Join(matches, ", "))
39 }
40}
41
42// MergeLine answers the question the review page answers, from the clone: does
43// this head still merge into its base? It says nothing it cannot check, and says
44// so when the head has not been fetched — a reviewer who has not run `git fetch`
45// should learn that rather than read an unchecked branch as clean.
46func MergeLine(b *Branch, r *metadata.Request) string {
47 if !r.Open() || r.Head == "" {
48 return ""
49 }
50 if head, _ := b.Repo.RevParse(r.Head); head == "" {
51 return fmt.Sprintf("cannot check: %s is not in this clone (git fetch %s)", format.ID(r.Head), b.Remote)
52 }
53 base := ""
54 for _, ref := range []string{"refs/remotes/" + b.Remote + "/" + r.Base, "refs/heads/" + r.Base} {
55 if sha, _ := b.Repo.RevParse(ref); sha != "" {
56 base = sha
57 break
58 }
59 }
60 if base == "" {
61 return ""
62 }
63 paths, err := b.Repo.MergeConflicts(base, r.Head)
64 if err != nil {
65 return ""
66 }
67 if len(paths) > 0 {
68 return fmt.Sprintf("CONFLICT with %s in %s — rebase before landing", r.Base, strings.Join(paths, ", "))
69 }
70 return fmt.Sprintf("clean against %s", r.Base)
71}
72
73// AnchorFor computes the anchor a review comment will carry: the blob of the file
74// on the side being reviewed, and a check that the line exists in it. It requires
75// the commits in the clone — the reviewer was looking at them — and says so rather
76// than writing an anchor it cannot compute.
77func AnchorFor(b *Branch, r *metadata.Request, path string, line int, side string) (*metadata.Anchor, error) {
78 if side != "new" && side != "old" {
79 return nil, fmt.Errorf("--side must be new (the head's file) or old (what it changed from)")
80 }
81 head, _ := b.Repo.RevParse(r.Head)
82 if head == "" {
83 return nil, fmt.Errorf("%s is not in this clone; git fetch %s before commenting on a line",
84 format.ID(r.Head), b.Remote)
85 }
86 commit := head
87 if side == "old" {
88 base := ""
89 for _, ref := range []string{"refs/remotes/" + b.Remote + "/" + r.Base, "refs/heads/" + r.Base} {
90 if sha, _ := b.Repo.RevParse(ref); sha != "" {
91 base = sha
92 break
93 }
94 }
95 if base == "" {
96 return nil, fmt.Errorf("cannot find %s in this clone; it is the old side of the review (git fetch %s)", r.Base, b.Remote)
97 }
98 mb, err := b.Repo.MergeBase(base, head)
99 if err != nil || mb == "" {
100 return nil, fmt.Errorf("cannot find where %s diverged from %s", r.Base, format.ID(head))
101 }
102 commit = mb
103 }
104 blob, err := b.Repo.RevParseAny(commit + ":" + path)
105 if err != nil || blob == "" {
106 return nil, fmt.Errorf("%s does not exist on the %s side (%s)", path, side, format.ID(commit))
107 }
108 content, err := b.Repo.BlobBySHA(blob)
109 if err != nil {
110 return nil, err
111 }
112 lines := strings.Count(string(content), "\n")
113 if !strings.HasSuffix(string(content), "\n") && len(content) > 0 {
114 lines++
115 }
116 if line > lines {
117 return nil, fmt.Errorf("%s has %d line(s) on the %s side; --line %d is past the end", path, lines, side, line)
118 }
119 return &metadata.Anchor{Path: path, Side: side, Line: line, Blob: blob}, nil
120}
121