Skip to main content

max / makenotwork

8.3 KB · 152 lines History Blame Raw
1 # Git Notes
2
3 Git has always been able to attach a note to a commit without rewriting it. Almost nobody uses the feature, because no forge renders notes and no clone fetches them. MNW renders them, lets you write them from the browser, and accepts pushes that plain git would reject.
4
5 Notes are ordinary git objects living on `refs/notes/*`. Everything MNW knows about a note is in your repository, not in a database only we can read. Clone the repo and the notes come with it.
6
7 ## Reading Notes
8
9 Nothing to set up. If a repository has notes, they appear:
10
11 - On the commit page, one panel per namespace, rendered as markdown.
12 - As a badge on commit log rows, so you can see which commits are annotated.
13 - On the Notes tab at `/git/username/repo-name/notes`, which lists every namespace with its count and a feed of annotations, newest first.
14 - On the file and blame views, alongside the commit being shown. An annotated line in blame links straight to its note.
15
16 ## Searching Notes
17
18 The Notes tab has a search box. It searches note text within the selected namespace, and understands quoted phrases and a leading `-` to exclude a word. There is a filter for notes on commits, which hides notes written on files and other objects.
19
20 Search reads an index MNW builds from your repository. The repository is the source of truth: the index can be dropped and rebuilt from the refs at any time, and nothing about a note is stored only there.
21
22 ## Following Notes As a Feed
23
24 Every repository has an RSS feed of its annotations at:
25
26 ```
27 /git/username/repo-name/notes.rss
28 ```
29
30 Add `?namespace=review` for one namespace. Items are ordered by when each annotation was written, so annotating an old commit is news. Private repositories have no feed.
31
32 ## Fetching Notes Into Your Clone
33
34 A clone does not fetch notes, and git offers no way to change that default. Set the refspec once per clone:
35
36 ```
37 git config --add remote.origin.fetch '+refs/notes/*:refs/notes/*'
38 ```
39
40 Every `git fetch` after that brings notes along. Read them with `git notes show <commit>`, or point git at a namespace other than the default with `git notes --ref=<namespace> show <commit>`.
41
42 This one line is most of why notes go unused everywhere. It is printed on the Notes tab of every repository so you never have to remember it.
43
44 ## Writing Notes From the Browser
45
46 Open any commit page in a repository you can push to. Each namespace panel has add, edit and delete controls, and you can create a new namespace by naming one.
47
48 Notes written this way are committed as `Your Display Name <username@users.makenot.work>`. Your account email address never enters the repository's object graph, because an address in a public repo is permanent and clonable by anyone who fetches the notes ref.
49
50 Write access follows push access: the repository owner and any collaborator with push permission.
51
52 ## Pushing Notes From Your Machine
53
54 Push notes to the inbox ref for the namespace:
55
56 ```
57 git push origin refs/notes/commits:refs/mnw/notes-inbox/commits
58 ```
59
60 Replace `commits` with your namespace on both sides. The server merges what you pushed into `refs/notes/commits` and reports the merged tip:
61
62 ```
63 notes: merged into refs/notes/commits
64 ```
65
66 Then fetch to see the result:
67
68 ```
69 git fetch origin
70 ```
71
72 Use this in place of `git push origin refs/notes/commits`. The plain push works when nobody else has written a note since you last fetched, and fails with a non-fast-forward rejection when somebody has. The inbox never fails, because the ref you are pushing to does not exist on the server: MNW deletes it as soon as it has merged your notes, so every push creates it fresh. There is no rejection to recover from and no case where the inbox is the wrong target, including the first push to a repository with no notes at all.
73
74 The merge uses git's `cat_sort_uniq` strategy. When two people annotate the same commit, both notes survive, sorted and deduplicated. Nothing is discarded and nothing is doubled if the same push is processed twice.
75
76 If the server does not answer, the push prints:
77
78 ```
79 notes: received, merge deferred (the server did not answer)
80 ```
81
82 Your notes are safe in the inbox ref and the next push to that namespace merges them.
83
84 ## Reading and Writing Notes From a Script
85
86 There is a JSON API under `/api/git/username/repo-name/notes`, for tooling that wants to annotate a commit without running git. A build script recording its result, a review bot recording a verdict.
87
88 Reads need no credential on a public repository:
89
90 ```
91 curl https://makenot.work/api/git/username/repo-name/notes
92 curl 'https://makenot.work/api/git/username/repo-name/notes/<commit>?namespace=review'
93 curl 'https://makenot.work/api/git/username/repo-name/notes/search?q=timeout'
94 ```
95
96 Writes need a personal access token with push permission, the same token `git push` over HTTPS uses. Create one under Access Tokens on your SSH keys settings page, and send it as the HTTP Basic password. The username half is ignored:
97
98 ```
99 curl -u x:$MNW_TOKEN -X PUT \
100 -H 'Content-Type: application/json' \
101 -d '{"namespace":"builds","content":"passed on 2 targets"}' \
102 https://makenot.work/api/git/username/repo-name/notes/<commit>
103
104 curl -u x:$MNW_TOKEN -X DELETE \
105 'https://makenot.work/api/git/username/repo-name/notes/<commit>?namespace=builds'
106 ```
107
108 A write is a real git commit on `refs/notes/<namespace>`, so a note written this way is fetched, read and pushed like any other. Two writers annotating the same commit are merged rather than one overwriting the other, and the response says `"merged": true` when that happened, meaning the stored note is not exactly what you sent.
109
110 A session cookie is not accepted for writes. A token is, which is what a script has.
111
112 Namespace and content rules are the browser's: `refs/notes/mnw/*` is refused, and content is capped at 50,000 characters.
113
114 Search is the one endpoint answered from the index rather than from your repository, since a full-text query cannot be served by walking a tree. Its response carries `"indexed": false` when MNW has not yet indexed the repository, so an empty result is never mistaken for no matches.
115
116 The full request and response shapes are in the [OpenAPI spec]https://makenot.work/api/openapi.json, under the Git Notes tag.
117
118 ## Namespaces
119
120 The default namespace is `commits`, which is what bare `git notes` uses. Any other name gives you a separate track of annotation on the same commits: `review`, `design`, `deploys`.
121
122 Namespace names may contain letters, numbers, hyphens, underscores, dots and slashes, up to 64 characters, and follow git's ref naming rules. A component cannot start with a dot, contain `..`, or end in `.lock`. Note content is capped at 50,000 characters.
123
124 `refs/notes/mnw/*` is reserved. It is where MNW writes its own metadata about your commits, and the web editor, the API and pushes all refuse it. Everything outside that prefix is yours.
125
126 ## What MNW Writes
127
128 Three namespaces under the reserved prefix carry metadata MNW computes about your repository. They are ordinary notes: they clone, they fetch, they read with `git notes --ref=mnw/builds show <commit>`, and they are yours to keep if you leave.
129
130 | Namespace | On | Says |
131 |---|---|---|
132 | `mnw/builds` | the commit a built tag points at | build status, version, tag, targets, and the error if it failed |
133 | `mnw/issues` | each commit whose message referenced an issue | which issues it closed, reopened or referenced |
134 | `mnw/scan` | the commit a release was built from | how that release's artifacts scanned |
135
136 Each note is a short list of `key: value` lines, readable at a terminal and parseable by a script.
137
138 These are read-only in the browser and refused on push, which is what "reserved" means in practice. MNW rewrites a note whole when the fact changes, so rebuilding a tag replaces its build note rather than adding a second one.
139
140 `mnw/scan` is written once every artifact in a release has a verdict, so it never reports a partial result. Its verdict is the worst verdict of any artifact in the release.
141
142 ## Annotating Files
143
144 Git notes can annotate any object, not only commits. A note on a blob is a note on one exact version of one file, and MNW shows it on that file's page. Most tools assume commits and quietly find nothing on the rest.
145
146 Notes on blobs are read-only in the browser for now. Write them with `git notes --ref=<namespace> add <blob-oid>` and push them the same way as any other note.
147
148 ## See Also
149
150 - [Git Source Browser]./git.md: browsing, cloning, issues and patches
151 - [Export]./export.md: taking your data with you
152