Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Code

392 commit(s) on main

Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys keenan · 1h ago 4b8c9e1d
history blame raw 3.5 KiB
1package index
2
3// The head summary: a repository's default branch tip, the language it is mostly
4// made of, and its newest commits.
5//
6// The landing and overview pages showed these from git on every request, and the
7// history and tree walks are the expensive part. They change only when a push
8// moves the default branch, so the push records them and a page reads rows.
9
10import (
11 "rodent.software/vole/forge/repo"
12 "rodent.software/vole/internal/lang"
13)
14
15type headSummary struct {
16 SHA string
17 Subject string
18 Author string
19 When int64
20 Language string
21 LangClass string
22}
23
24// RefreshHead recomputes a repository's default branch summary, its newest
25// commits and its language breakdown. It runs when a push lands (post-receive),
26// on a rebuild, and after the default branch changes. If the recorded SHA is
27// still the branch tip, nothing is written and changed is false: a tag or
28// metadata push then costs one rev-parse, not a tree walk.
29func (s *Store) RefreshHead(m *repo.Meta) (changed bool, err error) {
30 branch := m.DefaultBranch
31 if branch == "" {
32 // The hook only knows the directory and the id; the branch is the row's.
33 _ = s.db.QueryRow(`SELECT default_branch FROM repos WHERE id=?`, m.ID).Scan(&branch)
34 }
35 if branch == "" {
36 branch = m.Git().Symref()
37 }
38 sha := ""
39 if branch != "" {
40 if tip, err := m.Git().RevParse("refs/heads/" + branch); err == nil {
41 sha = tip
42 }
43 }
44 var stored string
45 if err := s.db.QueryRow(`SELECT head_sha FROM repos WHERE id=?`, m.ID).Scan(&stored); err != nil {
46 return false, err
47 }
48 if stored == sha {
49 return false, nil
50 }
51
52 var h headSummary
53 var commits []CommitRow
54 var shares []lang.Share
55 if sha != "" {
56 h.SHA = sha
57 if list, err := m.Git().Log(sha, 5); err == nil {
58 for i, c := range list {
59 if i == 0 {
60 h.Subject, h.Author, h.When = c.Subject, c.Author, c.When
61 }
62 commits = append(commits, CommitRow{SHA: c.SHA, Subject: c.Subject, Author: c.Author, When: c.When})
63 }
64 }
65 if sizes, err := m.Git().LsTreeSizes(sha); err == nil {
66 shares = lang.Breakdown(sizes)
67 h.Language, h.LangClass = lang.TopSizes(sizes)
68 }
69 }
70 return true, s.writeHead(m.ID, h, shares, commits)
71}
72
73// writeHead replaces the head row and the summaries that belong to it in one
74// transaction, so a reader never sees a new tip with an old tree.
75func (s *Store) writeHead(repoID string, h headSummary, shares []lang.Share, commits []CommitRow) error {
76 tx, err := s.db.Begin()
77 if err != nil {
78 return err
79 }
80 defer tx.Rollback()
81 if _, err := tx.Exec(
82 `UPDATE repos SET head_sha=?, head_subject=?, head_author=?, head_when=?, language=?, lang_class=? WHERE id=?`,
83 h.SHA, h.Subject, h.Author, h.When, h.Language, h.LangClass, repoID); err != nil {
84 return err
85 }
86 // The old summaries describe the old tip. Clearing them here means a
87 // failure to record the new ones shows a missing section, not yesterday's
88 // languages and commits under today's tip.
89 for _, table := range []string{"repo_languages", "repo_commits"} {
90 if _, err := tx.Exec(`DELETE FROM `+table+` WHERE repo_id=?`, repoID); err != nil {
91 return err
92 }
93 }
94 for _, sh := range shares {
95 if _, err := tx.Exec(
96 `INSERT INTO repo_languages(repo_id, name, bytes) VALUES(?,?,?)`,
97 repoID, sh.Name, sh.Bytes); err != nil {
98 return err
99 }
100 }
101 for i, c := range commits {
102 if _, err := tx.Exec(
103 `INSERT INTO repo_commits(repo_id, pos, sha, subject, author, when_at) VALUES(?,?,?,?,?,?)`,
104 repoID, i, c.SHA, c.Subject, c.Author, c.When); err != nil {
105 return err
106 }
107 }
108 return tx.Commit()
109}
110