reset.go RawHistoryFolder

1package recovery
2
3import (
4	"context"
5	"errors"
6	"fmt"
7
8	"github.com/bnema/vev/internal/domain"
9)
10
11// ResetIncompatible replaces an exact healthy checkpoint authority with a
12// fresh incarnation. Stale observations are harmless no-ops.
13func (c *Coordinator) ResetIncompatible(ctx context.Context, name string, expectedIncarnation domain.IncarnationID, expectedCheckpoint domain.CheckpointRef) (domain.CatalogueRecord, bool, error) {
14	if c == nil || c.catalogue == nil || c.repository == nil || c.locks == nil || c.random == nil {
15		return domain.CatalogueRecord{}, false, errors.New("recovery: incomplete incompatible-reset dependencies")
16	}
17	if err := ctx.Err(); err != nil {
18		return domain.CatalogueRecord{}, false, err
19	}
20	c.mutationMu.Lock()
21	defer c.mutationMu.Unlock()
22	unlock := c.locks.Lock([]string{name})
23	defer unlock()
24
25	record, ok, err := c.catalogue.Record(name)
26	if err != nil {
27		return domain.CatalogueRecord{}, false, err
28	}
29	if !ok || record.IncarnationID != expectedIncarnation || record.DegradedReason != "" ||
30		record.Committed == nil || *record.Committed != expectedCheckpoint {
31		return domain.CatalogueRecord{}, false, nil
32	}
33	return c.replaceIncarnationLocked(ctx, record, true, "reset")
34}
35
36// replaceIncarnationLocked commits a fresh catalogue authority before removing
37// the old repository incarnation. The caller must hold mutationMu and the
38// record name's KeyLock.
39func (c *Coordinator) replaceIncarnationLocked(ctx context.Context, record domain.CatalogueRecord, clearTabNames bool, operation string) (domain.CatalogueRecord, bool, error) {
40	old := record.IncarnationID
41	next, err := domain.NewIncarnationID(c.random)
42	if err != nil {
43		return domain.CatalogueRecord{}, false, fmt.Errorf("recovery: generate replacement incarnation: %w", err)
44	}
45	fresh := record
46	fresh.IncarnationID = next
47	fresh.Committed = nil
48	fresh.DegradedReason = ""
49	if clearTabNames {
50		fresh.TabNames = nil
51	}
52	if err := fresh.Validate(); err != nil {
53		return domain.CatalogueRecord{}, false, fmt.Errorf("recovery: invalid %s replacement: %w", operation, err)
54	}
55	if err := c.catalogue.Replace(record.Name, fresh); err != nil {
56		return domain.CatalogueRecord{}, false, err
57	}
58	if err := c.repository.DeleteIncarnation(ctx, old); err != nil {
59		return fresh, true, err
60	}
61	return fresh, true, nil
62}

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 12

Tags 11

↑↓ moveenter openesc close