detached e7b3a25

fsys.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3// Package fsys is the minimal file-system abstraction used by the store, with
4// an os-backed implementation. The faultfs sub-package provides an in-memory
5// implementation with fault injection for tests.
6package fsys
7
8import (
9	"errors"
10	"io"
11	"os"
12	"sort"
13	"syscall"
14)
15
16// ErrLocked is returned by FS.Lock when the lock is already held.
17var ErrLocked = errors.New("fsys: locked")
18
19// FS is a file system. Paths are used as given; implementations do not
20// resolve them against a working directory other than the OS one.
21type FS interface {
22	// OpenFile opens name with os.OpenFile flags. Files opened for writing
23	// always append: Write adds bytes at the current end of the file.
24	OpenFile(name string, flag int, perm os.FileMode) (File, error)
25	// Rename atomically replaces newpath with oldpath.
26	Rename(oldpath, newpath string) error
27	// Remove deletes a file.
28	Remove(name string) error
29	// ReadDir returns the sorted entry names of dir.
30	ReadDir(dir string) ([]string, error)
31	// MkdirAll creates dir and any missing parents.
32	MkdirAll(dir string, perm os.FileMode) error
33	// SyncDir makes the entries of dir (creations, renames, removals)
34	// durable.
35	SyncDir(dir string) error
36	// Lock takes an exclusive, non-blocking lock on the file at path,
37	// creating it if needed. It returns ErrLocked if the lock is held,
38	// including by the same process. Closing the result releases the lock.
39	Lock(path string) (io.Closer, error)
40}
41
42// File is an open file.
43type File interface {
44	// Write appends p to the end of the file.
45	Write(p []byte) (int, error)
46	ReadAt(p []byte, off int64) (int, error)
47	// Sync flushes data and the size needed to read it (fdatasync).
48	Sync() error
49	// SyncAll flushes data and all metadata (fsync).
50	SyncAll() error
51	Truncate(size int64) error
52	Size() (int64, error)
53	Close() error
54}
55
56// OS returns the FS backed by the operating system.
57func OS() FS { return osFS{} }
58
59type osFS struct{}
60
61func (osFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
62	if flag&(os.O_WRONLY|os.O_RDWR) != 0 {
63		flag |= os.O_APPEND
64	}
65	f, err := os.OpenFile(name, flag, perm)
66	if err != nil {
67		return nil, err
68	}
69	return osFile{f}, nil
70}
71
72func (osFS) Rename(oldpath, newpath string) error { return os.Rename(oldpath, newpath) }
73
74func (osFS) Remove(name string) error { return os.Remove(name) }
75
76func (osFS) ReadDir(dir string) ([]string, error) {
77	entries, err := os.ReadDir(dir)
78	if err != nil {
79		return nil, err
80	}
81	names := make([]string, len(entries))
82	for i, e := range entries {
83		names[i] = e.Name()
84	}
85	sort.Strings(names)
86	return names, nil
87}
88
89func (osFS) MkdirAll(dir string, perm os.FileMode) error { return os.MkdirAll(dir, perm) }
90
91func (osFS) SyncDir(dir string) error {
92	d, err := os.Open(dir)
93	if err != nil {
94		return err
95	}
96	err = d.Sync()
97	if cerr := d.Close(); err == nil {
98		err = cerr
99	}
100	return err
101}
102
103func (osFS) Lock(path string) (io.Closer, error) {
104	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0o600)
105	if err != nil {
106		return nil, err
107	}
108	if err := control(f, func(fd int) error {
109		return syscall.Flock(fd, syscall.LOCK_EX|syscall.LOCK_NB)
110	}); err != nil {
111		_ = f.Close()
112		if errors.Is(err, syscall.EWOULDBLOCK) {
113			return nil, ErrLocked
114		}
115		return nil, err
116	}
117	return f, nil // closing the descriptor releases the flock
118}
119
120type osFile struct{ *os.File }
121
122func (f osFile) Sync() error {
123	return control(f.File, syscall.Fdatasync)
124}
125
126func (f osFile) SyncAll() error { return f.File.Sync() }
127
128func (f osFile) Size() (int64, error) {
129	fi, err := f.Stat()
130	if err != nil {
131		return 0, err
132	}
133	return fi.Size(), nil
134}
135
136// control runs fn on the file descriptor, retrying on EINTR.
137func control(f *os.File, fn func(fd int) error) error {
138	rc, err := f.SyscallConn()
139	if err != nil {
140		return err
141	}
142	var ferr error
143	if err := rc.Control(func(fd uintptr) {
144		for {
145			ferr = fn(int(fd))
146			if !errors.Is(ferr, syscall.EINTR) {
147				return
148			}
149		}
150	}); err != nil {
151		return err
152	}
153	return ferr
154}

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