v0.6.0 d5cc8fe

routes.go RawHistoryFolder

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package ermitegit
4
5// DefaultRoutes returns the routes of production.
6//
7// The rule: a method is routed to ermite-git only when it passes the shared
8// conformance table (TestConformance, TestCompareWithCLI) and its benchmark
9// beats the CLI adapter in time, in bytes AND in allocations per call, warm
10// AND cold, on the generated fixture AND on lazygit (BenchmarkRepoReader,
11// ERMITE_GIT_BENCH_REPO). Where a method has several shapes, every shape must
12// win: the constants GetTree, GetTreeLastCommits, ResolveRevisionAbbrev and
13// ListCommitsPath cover directories of any size and files of any history. A
14// method that does not win stays on the CLI. GetCommit is not routable: the
15// diff stays on git. ListPaths has one shape: the fixture lists 131 files and
16// lazygit 2,180.
17//
18// Warm is a repository that is open; cold closes it before every call, the
19// price of the first read after the idle TTL, an eviction or a restart (a
20// cold ermite-git call opens the packs and the commit-graph: about 65 µs and
21// 130 allocations before the call itself). The CLI keeps nothing open, so its
22// cold run is its warm run. Only the allocations of the Go process are
23// counted, which favours the CLI (its work happens in git processes). The
24// parallel runs (b.RunParallel) were also made: they agree with the warm runs
25// and widen the time gap.
26//
27// The numbers are the median of six runs of `go test -bench RepoReader
28// -benchmem -benchtime 500ms` on an AMD Ryzen 9 7900X3D, git 2.56, ermite-git
29// 5228465, as "time bytes allocations per call", CLI against ermite-git (K and
30// M are KiB and MiB). The fixture has 220 commits, 8 directories of 40 files
31// and a 10 MiB blob; lazygit has 7,550 commits. "loses" names what the
32// library does not win: t time, b bytes, a allocations.
33//
34//	shape                   repo     mode  cli                  ermite-git           verdict
35//	ListRefs                fixture  warm  905µs 116.3K 125     24µs 19.0K 73        win
36//	ListRefs                fixture  cold  904µs 116.3K 125     88µs 35.2K 203       loses a
37//	ListRefs                lazygit  warm  901µs 105.2K 88      15µs 2.2K 27         win
38//	ListRefs                lazygit  cold  908µs 105.4K 88      88µs 14.2K 163       loses a
39//	ResolveRevision/branch  fixture  warm  22µs 0.9K 19         4µs 0.4K 8           win
40//	ResolveRevision/branch  fixture  cold  22µs 0.9K 19         68µs 16.6K 138       loses tba
41//	ResolveRevision/branch  lazygit  warm  22µs 1.0K 19         4µs 0.5K 8           win
42//	ResolveRevision/branch  lazygit  cold  23µs 1.0K 19         72µs 12.4K 144       loses tba
43//	ResolveRevision/fullid  fixture  warm  9µs 1.3K 22          2µs 0.4K 6           win
44//	ResolveRevision/fullid  fixture  cold  9µs 1.3K 22          65µs 16.6K 136       loses tba
45//	ResolveRevision/fullid  lazygit  warm  9µs 1.3K 22          2µs 0.4K 6           win
46//	ResolveRevision/fullid  lazygit  cold  9µs 1.3K 22          70µs 12.3K 142       loses tba
47//	ResolveRevision/abbrev  fixture  warm  63µs 1.5K 31         9µs 0.7K 11          win
48//	ResolveRevision/abbrev  fixture  cold  64µs 1.5K 31         71µs 16.8K 141       loses tba
49//	ResolveRevision/abbrev  lazygit  warm  63µs 1.5K 31         9µs 0.6K 11          win
50//	ResolveRevision/abbrev  lazygit  cold  63µs 1.5K 31         76µs 12.6K 147       loses tba
51//	ResolveRefPath          fixture  warm  104µs 2.4K 59        15µs 0.8K 22         win
52//	ResolveRefPath          fixture  cold  105µs 2.4K 59        78µs 17.0K 152       loses ba
53//	ResolveRefPath          lazygit  warm  150µs 4.1K 79        22µs 1.4K 29         win
54//	ResolveRefPath          lazygit  cold  148µs 4.1K 79        89µs 13.4K 165       loses ba
55//	GetTree/root            fixture  warm  717µs 108.4K 114     13µs 5.8K 24         win
56//	GetTree/root            fixture  cold  725µs 108.4K 114     78µs 22.4K 154       loses a
57//	GetTree/root            lazygit  warm  827µs 121.4K 167     29µs 18.2K 56        win
58//	GetTree/root            lazygit  cold  831µs 121.5K 167     376µs 1.6M 200       loses ba
59//	GetTree/dir             fixture  warm  742µs 109.5K 141     16µs 9.2K 26         win
60//	GetTree/dir             fixture  cold  745µs 109.5K 141     109µs 75.8K 158      loses a
61//	GetTree/dir             lazygit  warm  1.4ms 267.9K 1058    193µs 318.4K 73      loses b
62//	GetTree/dir             lazygit  cold  1.4ms 268.0K 1058    4.2ms 2.0M 2490      loses tba
63//	GetTree/root+last       fixture  warm  4.0ms 1.2M 1023      70µs 108.3K 107      win
64//	GetTree/root+last       fixture  cold  4.0ms 1.2M 1024      165µs 127.8K 249     win
65//	GetTree/root+last       lazygit  warm  20.2ms 3.8M 3072     6.1ms 367.0K 510     win
66//	GetTree/root+last       lazygit  cold  20.3ms 3.8M 3073     8.5ms 3.4M 4204      loses a
67//	GetTree/dir+last        fixture  warm  5.3ms 1.8M 1454      190µs 166.1K 142     win
68//	GetTree/dir+last        fixture  cold  5.4ms 1.8M 1455      316µs 247.6K 323     win
69//	GetTree/dir+last        lazygit  warm  195.2ms 20.5M 17116  7.9ms 1.5M 855       win
70//	GetTree/dir+last        lazygit  cold  195.9ms 20.5M 17107  14.8ms 5.1M 6989     win
71//	ListPaths               fixture  warm  760µs 118.4K 225     41µs 37.9K 30         win
72//	ListPaths               fixture  cold  771µs 118.4K 225     107µs 54.7K 160       win
73//	ListPaths               lazygit  warm  1.8ms 432.9K 2280    842µs 250.3K 477      win
74//	ListPaths               lazygit  cold  1.8ms 432.9K 2280    1.2ms 298.7K 749      win
75//	OpenBlob/small          fixture  warm  46µs 6.1K 25         17µs 5.8K 14         win
76//	OpenBlob/small          fixture  cold  47µs 6.1K 25         87µs 27.7K 147       loses tba
77//	OpenBlob/small          lazygit  warm  51µs 5.4K 25         29µs 7.0K 42         loses ba
78//	OpenBlob/small          lazygit  cold  50µs 5.4K 25         131µs 25.1K 184      loses tba
79//	OpenBlob/largest        fixture  warm  21.4ms 69.7K 64      52.5ms 9.0K 13       loses t
80//	OpenBlob/largest        fixture  cold  21.6ms 70.0K 64      51.4ms 26.2K 143     loses ta
81//	OpenBlob/largest        lazygit  warm  3.3ms 69.9K 64       5.7ms 16.3K 123      loses ta
82//	OpenBlob/largest        lazygit  cold  3.3ms 69.9K 64       5.8ms 31.6K 283      loses ta
83//	ListCommits/page1       fixture  warm  1.0ms 122.5K 404     61µs 19.1K 108       win
84//	ListCommits/page1       fixture  cold  1.0ms 122.5K 404     134µs 36.9K 244      win
85//	ListCommits/page1       lazygit  warm  1.3ms 157.2K 415     361µs 120.1K 224     win
86//	ListCommits/page1       lazygit  cold  1.3ms 157.2K 415     503µs 136.2K 377     win
87//	ListCommits/deep        fixture  warm  1.0ms 122.1K 404     71µs 19.3K 108       win
88//	ListCommits/deep        fixture  cold  1.0ms 122.1K 404     136µs 36.1K 238      win
89//	ListCommits/deep        lazygit  warm  2.0ms 123.2K 404     424µs 23.9K 112      win
90//	ListCommits/deep        lazygit  cold  2.0ms 123.2K 404     544µs 36.3K 248      win
91//	ListCommits/path        fixture  warm  1.6ms 108.8K 138     443µs 25.4K 42       win
92//	ListCommits/path        fixture  cold  1.6ms 108.9K 138     594µs 93.6K 360      loses a
93//	ListCommits/path        lazygit  warm  12.9ms 108.7K 118    6.0ms 29.2K 58       win
94//	ListCommits/path        lazygit  cold  12.8ms 107.7K 118    9.5ms 2.4M 6498      loses ba
95//	ListCommits/dirpath     fixture  warm  1.3ms 123.0K 403     199µs 33.8K 113      win
96//	ListCommits/dirpath     fixture  cold  1.3ms 123.0K 403     277µs 55.4K 268      win
97//	ListCommits/dirpath     lazygit  warm  6.7ms 127.8K 403     2.0ms 50.2K 181      win
98//	ListCommits/dirpath     lazygit  cold  6.7ms 127.8K 403     3.3ms 887.1K 2019    loses ba
99//
100// What decides the routes, with ermite-git 5228465 (zlib reader with a reused
101// adler32, header-size cache, Refresh and LookupRefID without allocations):
102//
103//   - The warm runs all win but GetTree of a large directory on lazygit (time
104//     is won since the zlib change, bytes are not) and OpenBlob, whose
105//     allocations come from the inflate of a small blob and whose time is
106//     lost for a 10 MiB blob (inflate speed: 52 ms against 21 ms).
107//   - The cold runs lose in allocations for every method that does little
108//     work after the open (ListRefs, GetTree, ResolveRefPath: 150 to 200
109//     against 60 to 140), and in time too for the ones that cost a few
110//     microseconds warm (ResolveRevision: 65 µs against 9 to 63 µs).
111//   - Only the history of a revision (ListCommits page 1 and deep) and the
112//     recursive path list (ListPaths) win every line, so they are the only
113//     methods routed. ListPaths wins even cold because one walk replaces a
114//     process and the parsing of its output (the bytes of the paths are kept in
115//     32 KiB blocks, which is what brought the lazygit bytes under the CLI's:
116//     a single growing buffer lost them, 699K against 433K). GetTree dir+last
117//     also wins every line, but GetTreeLastCommits covers GetTree root+last
118//     too, which loses cold allocations on lazygit (4204 against 3073): it is
119//     the nearest miss, and the first to flip once opening a repository
120//     allocates less.
121//   - OpenBlob by size: below 1 MiB the small blob loses cold and, on lazygit,
122//     warm bytes and allocations; above, it loses time. No size makes it win,
123//     so there is no size-based route.
124//
125// Once opening a repository allocates less, re-run the benchmark and flip the
126// constants it wins: ListRefs, ResolveRefPath and ResolveRevision win warm by 4
127// to 60 times in time.
128func DefaultRoutes() Routes {
129	return Routes{
130		ListRefs:              CLI,
131		ResolveRevision:       CLI,
132		ResolveRevisionAbbrev: CLI,
133		ResolveRefPath:        CLI,
134		GetTree:               CLI,
135		GetTreeLastCommits:    CLI,
136		OpenBlob:              CLI,
137		ListCommits:           ErmiteGit,
138		ListCommitsPath:       CLI,
139		ListPaths:             ErmiteGit,
140	}
141}

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 4

Tags 45

↑↓ moveenter openesc close