detached fda3c2b

protocol.go RawHistoryFolder

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package githttp
4
5import (
6	"encoding/json"
7	"errors"
8	"fmt"
9
10	"bnema.dev/ermite/internal/domain"
11)
12
13// Hook names, as used by git and by `ermite hook`.
14const (
15	HookPreReceive  = "pre-receive"
16	HookPostReceive = "post-receive"
17)
18
19// maxHookMessage bounds one line of the hook protocol, request or reply.
20const maxHookMessage = 32 << 20
21
22// HookRequest is the one JSON line a hook sends over the socket.
23//
24//	{"token":"<hook token>","hook":"pre-receive","updates":[{"old":"<oid>","new":"<oid>","ref":"refs/heads/main"}]}
25//
26// The repository is not sent: the token is bound to it.
27type HookRequest struct {
28	Token   string       `json:"token"`
29	Hook    string       `json:"hook"`
30	Updates []HookUpdate `json:"updates"`
31	// ObjectsDir and Alternates are GIT_OBJECT_DIRECTORY and
32	// GIT_ALTERNATE_OBJECT_DIRECTORIES of the hook: where the objects of a push
33	// in quarantine are (pre-receive only).
34	ObjectsDir string `json:"objects_dir,omitempty"`
35	Alternates string `json:"alternates,omitempty"`
36}
37
38// HookUpdate is one ref update, as git feeds it to the hook.
39type HookUpdate struct {
40	Old string `json:"old"`
41	New string `json:"new"`
42	Ref string `json:"ref"`
43}
44
45// HookReply is the one JSON line ermite answers. Message is shown to the
46// pusher (on stderr of the hook, so git prefixes it with "remote:").
47//
48//	{"ok":true}  {"ok":false,"message":"push rejected: ..."}
49type HookReply struct {
50	OK      bool   `json:"ok"`
51	Message string `json:"message,omitempty"`
52}
53
54// refUpdates checks the request fields that do not need the token.
55func (r HookRequest) refUpdates() ([]domain.RefUpdate, error) {
56	switch r.Hook {
57	case HookPreReceive, HookPostReceive:
58	default:
59		return nil, fmt.Errorf("unknown hook %q", r.Hook)
60	}
61	if r.Token == "" {
62		return nil, errors.New("missing token")
63	}
64	out := make([]domain.RefUpdate, 0, len(r.Updates))
65	for _, u := range r.Updates {
66		up, err := domain.NewRefUpdate(u.Old, u.New, u.Ref)
67		if err != nil {
68			return nil, err
69		}
70		out = append(out, up)
71	}
72	return out, nil
73}
74
75func encodeLine(v any) ([]byte, error) {
76	b, err := json.Marshal(v)
77	if err != nil {
78		return nil, err
79	}
80	return append(b, '\n'), nil
81}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 45

↑↓ moveenter openesc close