v0.1.0 5228465

lastcommit_test.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3package git
4
5import (
6	"context"
7	"fmt"
8	"slices"
9	"testing"
10)
11
12// lastOracle runs git log -n 1 for each name.
13func (w *wfix) lastOracle(tip int, dir string, names []string) []ObjectID {
14	w.t.Helper()
15	out := make([]ObjectID, len(names))
16	for i, n := range names {
17		p := n
18		if dir != "" {
19			p = dir + "/" + n
20		}
21		if l := lines(w.git("", "-c", "core.commitGraph=false", "log", "-n", "1", "--format=%H", w.ids[tip], "--", p)); len(l) > 0 {
22			out[i] = mustID(w.t, l[0])
23		}
24	}
25	return out
26}
27
28var lastDirs = map[string][]string{
29	"":      {"a", "b", "m", "d", "q", "nope", "gone", "readded", "x", "a"},
30	"d":     {"x", "y", "e", "nope"},
31	"d/e":   {"z", "w", "nope"},
32	"d/":    {"x", "e"},
33	"./d/e": {"z"},
34	"nodir": {"a", "x"},
35}
36
37func checkLast(t *testing.T, w *wfix, tips []int) {
38	t.Helper()
39	type key struct {
40		tip int
41		dir string
42	}
43	want := map[key][]ObjectID{}
44	for _, tip := range tips {
45		for dir, names := range lastDirs {
46			want[key{tip, dir}] = w.lastOracle(tip, dir, names)
47		}
48	}
49	for _, g := range wgraphs {
50		t.Run(g.name, func(t *testing.T) {
51			w.setGraph(g)
52			r, err := Open(w.dir)
53			if err != nil {
54				t.Fatal(err)
55			}
56			defer func() { _ = r.Close() }()
57			for _, tip := range tips {
58				for dir, names := range lastDirs {
59					got, err := r.LastCommits(context.Background(), mustID(t, w.ids[tip]), dir, names)
60					if err != nil {
61						t.Fatal(err)
62					}
63					if exp := want[key{tip, dir}]; !slices.Equal(got, exp) {
64						t.Fatalf("tip %d dir %q:\n got %v\nwant %v", tip, dir, got, exp)
65					}
66				}
67			}
68		})
69	}
70}
71
72func TestLastCommitsRandom(t *testing.T) {
73	requireGit(t)
74	seeds := []int64{1, 2, 3, 4, 5, 6, 7, 8}
75	if testing.Short() {
76		seeds = seeds[:2]
77	}
78	for _, seed := range seeds {
79		t.Run(fmt.Sprintf("seed%d", seed), func(t *testing.T) {
80			cs := randomHistory(seed, 140)
81			checkLast(t, buildWFix(t, cs), lastTips(len(cs)))
82		})
83	}
84}
85
86func TestLastCommitsHandcrafted(t *testing.T) {
87	requireGit(t)
88	for name, cs := range handHistories() {
89		t.Run(name, func(t *testing.T) {
90			tips := make([]int, len(cs))
91			for i := range tips {
92				tips[i] = i
93			}
94			checkLast(t, buildWFix(t, cs), tips)
95		})
96	}
97}
98
99func TestLastCommitSingle(t *testing.T) {
100	requireGit(t)
101	cs := randomHistory(9, 80)
102	w := buildWFix(t, cs)
103	w.setGraph(wgraphs[4])
104	r, err := Open(w.dir)
105	if err != nil {
106		t.Fatal(err)
107	}
108	defer func() { _ = r.Close() }()
109	tip := len(cs) - 1
110	for _, p := range []string{"a", "d", "d/e/z", "nope", ""} {
111		id, ok, err := r.LastCommit(context.Background(), mustID(t, w.ids[tip]), p)
112		if err != nil {
113			t.Fatal(err)
114		}
115		exp := w.gitLog(tip, 0, 1, p)
116		if ok != (len(exp) == 1) || (ok && id.String() != exp[0]) {
117			t.Errorf("%q: got %v %v, want %v", p, id, ok, exp)
118		}
119	}
120	for _, bad := range []string{"", ".", "a/b", "../x"} {
121		if _, err := r.LastCommits(context.Background(), mustID(t, w.ids[tip]), "", []string{bad}); !errorsIs(err, ErrInvalidPath) {
122			t.Errorf("name %q: err = %v", bad, err)
123		}
124	}
125	if got, err := r.LastCommits(context.Background(), mustID(t, w.ids[tip]), "", nil); err != nil || len(got) != 0 {
126		t.Errorf("no names: %v %v", got, err)
127	}
128}

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 1

Tags 2

↑↓ moveenter openesc close